[grilo] net: re-structure code



commit f08caabfb7d4d1aa69eafef294bb55fcb6d65677
Author: Juan A. Suarez Romero <jasuarez igalia com>
Date:   Fri Jun 14 19:56:19 2013 +0200

    net: re-structure code
    
    As we have rid of stable/unstable code due the new libsoup version
    requirement, let's merge some files.

 libs/net/Makefile.am       |    3 -
 libs/net/grl-net-mock.c    |    1 -
 libs/net/grl-net-private.c |  175 ----------------
 libs/net/grl-net-private.h |   95 ---------
 libs/net/grl-net-soup.c    |  356 ---------------------------------
 libs/net/grl-net-wc.c      |  471 +++++++++++++++++++++++++++++++++++++++++++-
 po/POTFILES.in             |    4 +-
 7 files changed, 467 insertions(+), 638 deletions(-)
---
diff --git a/libs/net/Makefile.am b/libs/net/Makefile.am
index f4113d1..035477e 100644
--- a/libs/net/Makefile.am
+++ b/libs/net/Makefile.am
@@ -13,8 +13,6 @@ libgrlnet_ GRL_MAJORMINOR@_la_DEPENDENCIES =  \
 
 libgrlnet_ GRL_MAJORMINOR@_la_SOURCES =   \
        grl-net-mock.c                         \
-       grl-net-private.c                      \
-       grl-net-soup.c                         \
        grl-net-wc.c
 
 libgrlnet_ GRL_MAJORMINOR@_la_CFLAGS = \
@@ -46,7 +44,6 @@ libgrlnet_ GRL_MAJORMINOR@include_HEADERS =   \
        grl-net.h
 
 noinst_HEADERS =               \
-       grl-net-private.h       \
        grl-net-mock-private.h
 
 MAINTAINERCLEANFILES = \
diff --git a/libs/net/grl-net-mock.c b/libs/net/grl-net-mock.c
index 6b51e33..3b4c6d7 100644
--- a/libs/net/grl-net-mock.c
+++ b/libs/net/grl-net-mock.c
@@ -35,7 +35,6 @@
 #include <grl-log.h>
 
 #include "grl-net-mock-private.h"
-#include "grl-net-private.h"
 
 static GKeyFile *config = NULL;
 static GRegex *ignored_parameters = NULL;
diff --git a/libs/net/grl-net-wc.c b/libs/net/grl-net-wc.c
index 95f0640..0490bf8 100644
--- a/libs/net/grl-net-wc.c
+++ b/libs/net/grl-net-wc.c
@@ -39,15 +39,28 @@
 #include "config.h"
 #endif
 
-#include <string.h>
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include <errno.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+#include <libsoup/soup-cache.h>
+#include <libsoup/soup-request-http.h>
 #include <libsoup/soup.h>
+#include <string.h>
+
+#ifndef LIBSOUP_REQUESTER_DEPRECATED
+#include <libsoup/soup-requester.h>
+#endif
 
 #include <grilo.h>
 #include "grl-net-wc.h"
-#include "grl-net-private.h"
 #include "grl-net-mock-private.h"
 
-GRL_LOG_DOMAIN(wc_log_domain);
+#define GRL_LOG_DOMAIN_DEFAULT wc_log_domain
+GRL_LOG_DOMAIN_STATIC(wc_log_domain);
+
+#define GRL_NET_CAPTURE_DIR_VAR "GRL_NET_CAPTURE_DIR"
 
 enum {
   PROP_0,
@@ -58,6 +71,32 @@ enum {
   PROP_USER_AGENT
 };
 
+struct request_res {
+  SoupRequest *request;
+  gchar *buffer;
+  gsize length;
+  gsize offset;
+};
+
+struct _GrlNetWcPrivate {
+  SoupSession *session;
+  SoupLoggerLogLevel log_level;
+  /* throttling in secs */
+  guint throttling;
+  /* last request time  */
+  GTimeVal last_request;
+  /* closure queue for delayed requests */
+  GQueue *pending;
+  /* cache size in Mb */
+  guint cache_size;
+#ifndef LIBSOUP_REQUESTER_DEPRECATED
+  void *requester;
+#endif
+  gchar *previous_data;
+};
+
+static const char *capture_dir = NULL;
+
 #define GRL_NET_WC_GET_PRIVATE(object)                 \
   (G_TYPE_INSTANCE_GET_PRIVATE((object),                \
                                GRL_TYPE_NET_WC,                \
@@ -165,6 +204,15 @@ grl_net_wc_class_init (GrlNetWcClass *klass)
                                                         G_PARAM_STATIC_STRINGS));
 }
 
+static void
+free_op_res (void *op)
+{
+  struct request_res *rr = op;
+
+  g_object_unref (rr->request);
+  g_slice_free (struct request_res, rr);
+}
+
 /*
  * use-thread-context is available for libsoup-2.4 >= 2.39.0
  * We check in run-time if it's available
@@ -181,6 +229,109 @@ set_thread_context (GrlNetWc *self)
 }
 
 static void
+init_dump_directory (void)
+{
+  capture_dir = g_getenv (GRL_NET_CAPTURE_DIR_VAR);
+
+  if (capture_dir && is_mocked ()) {
+    GRL_WARNING ("Cannot capture while mocking is enabled.");
+    capture_dir = NULL;
+    return;
+  }
+
+  if (capture_dir && g_mkdir_with_parents (capture_dir, 0700) != 0) {
+    GRL_WARNING ("Could not create capture directory \"%s\": %s",
+                 capture_dir, g_strerror (errno));
+    capture_dir = NULL;
+    return;
+  }
+}
+
+static void
+cache_down (GrlNetWc *self)
+{
+  GFile *cache_dir_file;
+  GrlNetWcPrivate *priv = self->priv;
+  SoupSessionFeature *cache = soup_session_get_feature (priv->session, SOUP_TYPE_CACHE);
+  gchar *cache_dir;
+
+  GRL_DEBUG ("cache down");
+
+  if (!cache) {
+    return;
+  }
+
+  soup_cache_clear (SOUP_CACHE (cache));
+
+  g_object_get (cache, "cache-dir", &cache_dir, NULL);
+  cache_dir_file = g_file_new_for_path (cache_dir);
+  g_free (cache_dir);
+
+  g_file_delete (cache_dir_file, NULL, NULL);
+  g_object_unref (G_OBJECT (cache_dir_file));
+
+  soup_session_remove_feature (priv->session, cache);
+}
+
+static void
+cache_up (GrlNetWc *self)
+{
+  SoupCache *cache;
+  GrlNetWcPrivate *priv = self->priv;
+  gchar *dir;
+
+  GRL_DEBUG ("cache up");
+
+  dir = g_dir_make_tmp ("grilo-plugin-cache-XXXXXX", NULL);
+  if (!dir)
+    return;
+
+  cache = soup_cache_new (dir, SOUP_CACHE_SINGLE_USER);
+  g_free (dir);
+
+  soup_session_add_feature (priv->session,
+                            SOUP_SESSION_FEATURE (cache));
+
+  if (priv->cache_size) {
+    soup_cache_set_max_size (cache, priv->cache_size * 1024 * 1024);
+  }
+
+  g_object_unref (cache);
+}
+
+static gboolean
+cache_is_available (GrlNetWc *self)
+{
+  return soup_session_get_feature (self->priv->session, SOUP_TYPE_CACHE) != NULL;
+}
+
+static void
+init_requester (GrlNetWc *self)
+{
+#ifndef LIBSOUP_REQUESTER_DEPRECATED
+  GrlNetWcPrivate *priv = self->priv;
+
+  priv->requester = soup_requester_new ();
+  soup_session_add_feature (priv->session,
+                            SOUP_SESSION_FEATURE (priv->requester));
+#endif
+  init_dump_directory ();
+}
+
+static void
+finalize_requester (GrlNetWc *self)
+{
+  GrlNetWcPrivate *priv = self->priv;
+
+  cache_down (self);
+  g_free (priv->previous_data);
+
+#ifndef LIBSOUP_REQUESTER_DEPRECATED
+  g_object_unref (priv->requester);
+#endif
+}
+
+static void
 grl_net_wc_init (GrlNetWc *wc)
 {
   GRL_LOG_DOMAIN_INIT (wc_log_domain, "wc");
@@ -267,7 +418,7 @@ grl_net_wc_get_property (GObject *object,
     g_value_set_boolean(value, cache_is_available (wc));
     break;
   case PROP_CACHE_SIZE:
-    g_value_set_uint (value, cache_get_size (wc));
+    g_value_set_uint (value, wc->priv->cache_size);
     break;
   case PROP_USER_AGENT:
     g_object_get_property (G_OBJECT (wc->priv->session), "user_agent", value);
@@ -298,6 +449,277 @@ request_clos_destroy (gpointer data)
   g_free (c);
 }
 
+static void
+parse_error (guint status,
+             const gchar *reason,
+             const gchar *response,
+             GSimpleAsyncResult *result)
+{
+  if (!response || *response == '\0')
+    response = reason;
+
+  switch (status) {
+  case SOUP_STATUS_CANT_RESOLVE:
+  case SOUP_STATUS_CANT_CONNECT:
+  case SOUP_STATUS_SSL_FAILED:
+  case SOUP_STATUS_IO_ERROR:
+    g_simple_async_result_set_error (result, GRL_NET_WC_ERROR,
+                                     GRL_NET_WC_ERROR_NETWORK_ERROR,
+                                     _("Cannot connect to the server"));
+    return;
+  case SOUP_STATUS_CANT_RESOLVE_PROXY:
+  case SOUP_STATUS_CANT_CONNECT_PROXY:
+    g_simple_async_result_set_error (result, GRL_NET_WC_ERROR,
+                                     GRL_NET_WC_ERROR_PROXY_ERROR,
+                                     _("Cannot connect to the proxy server"));
+    return;
+  case SOUP_STATUS_INTERNAL_SERVER_ERROR: /* 500 */
+  case SOUP_STATUS_MALFORMED:
+  case SOUP_STATUS_BAD_REQUEST: /* 400 */
+    g_simple_async_result_set_error (result, GRL_NET_WC_ERROR,
+                                     GRL_NET_WC_ERROR_PROTOCOL_ERROR,
+                                     _("Invalid request URI or header: %s"),
+                                     response);
+    return;
+  case SOUP_STATUS_UNAUTHORIZED: /* 401 */
+  case SOUP_STATUS_FORBIDDEN: /* 403 */
+    g_simple_async_result_set_error (result, GRL_NET_WC_ERROR,
+                                     GRL_NET_WC_ERROR_AUTHENTICATION_REQUIRED,
+                                     _("Authentication required: %s"), response);
+    return;
+  case SOUP_STATUS_NOT_FOUND: /* 404 */
+    g_simple_async_result_set_error (result, GRL_NET_WC_ERROR,
+                                     GRL_NET_WC_ERROR_NOT_FOUND,
+                                     _("The requested resource was not found: %s"),
+                                     response);
+    return;
+  case SOUP_STATUS_CONFLICT: /* 409 */
+  case SOUP_STATUS_PRECONDITION_FAILED: /* 412 */
+    g_simple_async_result_set_error (result, GRL_NET_WC_ERROR,
+                                     GRL_NET_WC_ERROR_CONFLICT,
+                                     _("The entry has been modified since it was downloaded: %s"),
+                                     response);
+    return;
+  case SOUP_STATUS_CANCELLED:
+    g_simple_async_result_set_error (result, GRL_NET_WC_ERROR,
+                                     GRL_NET_WC_ERROR_CANCELLED,
+                                     _("Operation was cancelled"));
+    return;
+  default:
+    g_message (_("Unhandled status: %s"), soup_status_get_phrase (status));
+  }
+}
+
+static char *
+build_request_filename (const char *uri)
+{
+  char *hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1);
+
+  char *filename = g_strdup_printf ("%"G_GINT64_FORMAT "-%s.data",
+                                    g_get_monotonic_time (), hash);
+
+  g_free (hash);
+  return filename;
+}
+
+static void
+dump_data (SoupURI *uri,
+           const char *buffer,
+           const gsize length)
+{
+  if (!capture_dir)
+    return;
+
+  char *uri_string = soup_uri_to_string (uri, FALSE);
+
+  /* Write request content to file in capture directory. */
+  char *request_filename = build_request_filename (uri_string);
+  char *path = g_build_filename (capture_dir, request_filename, NULL);
+
+  GError *error = NULL;
+  if (!g_file_set_contents (path, buffer, length, &error)) {
+    GRL_WARNING ("Could not write contents to disk: %s", error->message);
+    g_error_free (error);
+  }
+
+  g_free (path);
+
+  /* Append record about the just written file to "grl-net-mock-data-%PID.ini"
+   * in the capture directory. */
+  char *filename = g_strdup_printf ("grl-net-mock-data-%u.ini", getpid());
+  path = g_build_filename (capture_dir, filename, NULL);
+  g_free (filename);
+
+  FILE *stream = g_fopen (path, "at");
+  g_free (path);
+
+  if (!stream) {
+    GRL_WARNING ("Could not write contents to disk: %s", g_strerror (errno));
+  } else {
+    if (ftell (stream) == 0)
+      fprintf (stream, "[default]\nversion=%d\n\n", GRL_NET_MOCK_VERSION);
+
+    fprintf (stream, "[%s]\ndata=%s\n\n", uri_string, request_filename);
+    fclose (stream);
+  }
+
+  g_free (request_filename);
+  g_free (uri_string);
+}
+
+static void
+read_async_cb (GObject *source,
+               GAsyncResult *res,
+               gpointer user_data)
+{
+  GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
+  struct request_res *rr = g_simple_async_result_get_op_res_gpointer (result);;
+
+  GError *error = NULL;
+  gssize s = g_input_stream_read_finish (G_INPUT_STREAM (source), res, &error);
+
+  gsize to_read;
+
+  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);
+    g_object_unref (result);
+    return;
+  }
+
+  {
+    SoupMessage *msg =
+      soup_request_http_get_message (SOUP_REQUEST_HTTP (rr->request));
+
+    if (msg && 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);
+  g_object_unref (result);
+}
+
+static void
+reply_cb (GObject *source,
+          GAsyncResult *res,
+          gpointer user_data)
+{
+  GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
+  struct request_res *rr = g_simple_async_result_get_op_res_gpointer (result);
+
+  GError *error = NULL;
+  GInputStream *in = soup_request_send_finish (rr->request, res, &error);
+
+  if (error) {
+    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);
+    g_object_unref (result);
+    return;
+  }
+
+  rr->length = soup_request_get_content_length (rr->request) + 1;
+  if (rr->length == 1)
+    rr->length = 50 * 1024;
+
+  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);
+}
+
+static void
+get_url_now (GrlNetWc *self,
+             const char *url,
+             GHashTable *headers,
+             GAsyncResult *result,
+             GCancellable *cancellable)
+{
+  GrlNetWcPrivate *priv = self->priv;
+  struct request_res *rr = g_slice_new0 (struct request_res);
+
+  g_simple_async_result_set_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result),
+                                             rr,
+                                             NULL);
+
+#ifdef LIBSOUP_REQUESTER_DEPRECATED
+  SoupURI *uri = soup_uri_new (url);
+  rr->request = soup_session_request_uri (priv->session, uri, NULL);
+  soup_uri_free (uri);
+#else
+  rr->request = soup_requester_request (priv->requester, url, NULL);
+#endif
+
+  if (headers != NULL) {
+    SoupMessage *message;
+    GHashTableIter iter;
+    const char *key, *value;
+
+    message = soup_request_http_get_message (SOUP_REQUEST_HTTP (rr->request));
+
+    if (message) {
+      g_hash_table_iter_init (&iter, headers);
+      while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *)&value)) {
+        soup_message_headers_append (message->request_headers, key, value);
+      }
+      g_object_unref (message);
+    }
+  }
+
+  soup_request_send_async (rr->request, cancellable, reply_cb, result);
+}
+
 static gboolean
 get_url_cb (gpointer user_data)
 {
@@ -357,6 +779,36 @@ get_url (GrlNetWc *self,
   g_queue_push_head (self->priv->pending, c);
 }
 
+static void
+get_content (GrlNetWc *self,
+             void *op,
+             gchar **content,
+             gsize *length)
+{
+  GrlNetWcPrivate *priv = self->priv;
+  struct request_res *rr = op;
+
+  dump_data (soup_request_get_uri (rr->request),
+             rr->buffer,
+             rr->offset);
+
+  if (priv->previous_data)
+    g_free (priv->previous_data);
+
+  priv->previous_data = rr->buffer;
+
+  if (content)
+    *content = self->priv->previous_data;
+  else {
+    g_free (rr->buffer);
+    self->priv->previous_data = NULL;
+    rr->buffer = NULL;
+  }
+
+  if (length)
+    *length = rr->offset;
+}
+
 /**
  * grl_net_wc_new:
  *
@@ -637,7 +1089,16 @@ grl_net_wc_set_cache_size (GrlNetWc *self,
 {
   g_return_if_fail (GRL_IS_NET_WC (self));
 
-  cache_set_size (self, size);
+  if (self->priv->cache_size == size)
+    return;
+
+  self->priv->cache_size = size;
+
+  SoupSessionFeature *cache = soup_session_get_feature (self->priv->session, SOUP_TYPE_CACHE);
+  if (!cache)
+    return;
+
+  soup_cache_set_max_size (SOUP_CACHE (cache), size * 1024 * 1024);
 }
 
 /**
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e682287..8d0b471 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,7 +1,5 @@
 libs/net/grl-net-mock.c
-libs/net/grl-net-private.c
-libs/net/grl-net-soup-stable.c
-libs/net/grl-net-soup-unstable.c
+libs/net/grl-net-wc.c
 src/grilo.c
 src/grl-multiple.c
 src/grl-registry.c


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]