soup-request: Add support to handle gresource URI requests
authorCarlos Garcia Campos <cgarcia@igalia.com>
Sun, 26 Aug 2012 13:20:34 +0000 (15:20 +0200)
committerCarlos Garcia Campos <carlosgc@gnome.org>
Sat, 20 Oct 2012 09:34:18 +0000 (11:34 +0200)
GFile already supports gresource when using g_file_new_for_uri() with a
resource:// URI. We can add "resource" as a valid scheme for
SoupRequestFile and make sure the GFile is created with the gresource
URI for gresource requests.

https://bugzilla.gnome.org/show_bug.cgi?id=682721

configure.ac
docs/reference/libsoup-2.4-sections.txt
libsoup/soup-request-file.c
libsoup/soup-uri.c
libsoup/soup-uri.h
tests/Makefile.am
tests/resource-test.c [new file with mode: 0644]
tests/soup-tests.gresource.xml [new file with mode: 0644]

index f094587..a6b2b70 100644 (file)
@@ -307,6 +307,9 @@ else
 fi
 AM_CONDITIONAL(HAVE_CURL, test "$CURL" != no)
 
+GLIB_COMPILE_RESOURCES=`$PKG_CONFIG --variable glib_compile_resources gio-2.0`
+AC_SUBST(GLIB_COMPILE_RESOURCES)
+
 AC_SUBST(MISSING_REGRESSION_TEST_PACKAGES)
 AM_CONDITIONAL(MISSING_REGRESSION_TEST_PACKAGES, test -n "$MISSING_REGRESSION_TEST_PACKAGES")
 
index 6f819c3..961e416 100644 (file)
@@ -631,6 +631,7 @@ SOUP_URI_SCHEME_HTTPS
 SOUP_URI_SCHEME_DATA
 SOUP_URI_SCHEME_FILE
 SOUP_URI_SCHEME_FTP
+SOUP_URI_SCHEME_RESOURCE
 soup_uri_uses_default_port
 SOUP_URI_IS_VALID
 SOUP_URI_VALID_FOR_HTTP
index 0b5638d..73d0cd8 100644 (file)
@@ -128,7 +128,15 @@ soup_request_file_ensure_file (SoupRequestFile  *file,
        windowsify_file_uri_path (decoded_path);
 #endif
 
-       file->priv->gfile = g_file_new_for_path (decoded_path);
+       if (uri->scheme == SOUP_URI_SCHEME_RESOURCE) {
+               char *uri_str;
+
+               uri_str = g_strdup_printf ("resource://%s", decoded_path);
+               file->priv->gfile = g_file_new_for_uri (uri_str);
+               g_free (uri_str);
+       } else
+               file->priv->gfile = g_file_new_for_path (decoded_path);
+
        g_free (decoded_path);
        return TRUE;
 }
@@ -250,7 +258,7 @@ soup_request_file_get_content_type (SoupRequest *request)
        return file->priv->mime_type;
 }
 
-static const char *file_schemes[] = { "file", NULL };
+static const char *file_schemes[] = { "file", "resource", NULL };
 
 static void
 soup_request_file_class_init (SoupRequestFileClass *request_file_class)
index 4be679d..d19290b 100644 (file)
@@ -110,7 +110,7 @@ static char *uri_normalized_copy (const char *str, int length, const char *unesc
 
 gpointer _SOUP_URI_SCHEME_HTTP, _SOUP_URI_SCHEME_HTTPS;
 gpointer _SOUP_URI_SCHEME_FTP;
-gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA;
+gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA, _SOUP_URI_SCHEME_RESOURCE;
 
 static inline const char *
 soup_uri_parse_scheme (const char *scheme, int len)
@@ -119,6 +119,8 @@ soup_uri_parse_scheme (const char *scheme, int len)
                return SOUP_URI_SCHEME_HTTP;
        } else if (len == 5 && !g_ascii_strncasecmp (scheme, "https", len)) {
                return SOUP_URI_SCHEME_HTTPS;
+       } else if (len == 8 && !g_ascii_strncasecmp (scheme, "resource", len)) {
+               return SOUP_URI_SCHEME_RESOURCE;
        } else {
                char *lower_scheme;
 
@@ -815,6 +817,15 @@ soup_uri_uses_default_port (SoupURI *uri)
  **/
 
 /**
+ * SOUP_URI_SCHEME_RESOURCE:
+ *
+ * "resource" as an interned string. This can be compared directly
+ * against the value of a #SoupURI's <structfield>scheme</structfield>
+ *
+ * Since: 2.42
+ **/
+
+/**
  * soup_uri_get_scheme:
  * @uri: a #SoupURI
  *
index b851dbe..e2195b1 100644 (file)
@@ -31,14 +31,15 @@ struct _SoupURI {
 GType       soup_uri_get_type              (void);
 #define SOUP_TYPE_URI (soup_uri_get_type ())
 
-#define SOUP_URI_SCHEME_HTTP  _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTP, "http")
-#define SOUP_URI_SCHEME_HTTPS _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTPS, "https")
-#define SOUP_URI_SCHEME_FTP   _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_FTP, "ftp")
-#define SOUP_URI_SCHEME_FILE  _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_FILE, "file")
-#define SOUP_URI_SCHEME_DATA  _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_DATA, "data")
+#define SOUP_URI_SCHEME_HTTP     _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTP, "http")
+#define SOUP_URI_SCHEME_HTTPS    _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTPS, "https")
+#define SOUP_URI_SCHEME_FTP      _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_FTP, "ftp")
+#define SOUP_URI_SCHEME_FILE     _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_FILE, "file")
+#define SOUP_URI_SCHEME_DATA     _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_DATA, "data")
+#define SOUP_URI_SCHEME_RESOURCE _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_RESOURCE, "resource")
 extern gpointer _SOUP_URI_SCHEME_HTTP, _SOUP_URI_SCHEME_HTTPS;
 extern gpointer _SOUP_URI_SCHEME_FTP;
-extern gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA;
+extern gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA, _SOUP_URI_SCHEME_RESOURCE;
 
 SoupURI           *soup_uri_new_with_base         (SoupURI    *base,
                                            const char *uri_string);
index d2a1cd6..085a81b 100644 (file)
@@ -28,6 +28,7 @@ noinst_PROGRAMS =     \
        ntlm-test       \
        redirect-test   \
        requester-test  \
+       resource-test   \
        simple-httpd    \
        simple-proxy    \
        sniffing-test   \
@@ -41,6 +42,8 @@ noinst_PROGRAMS =     \
        $(APACHE_TESTS) \
        $(XMLRPC_TESTS)
 
+noinst_DATA = soup-tests.gresource
+
 TEST_SRCS = test-utils.c test-utils.h
 
 auth_test_SOURCES = auth-test.c $(TEST_SRCS)
@@ -66,6 +69,7 @@ pull_api_SOURCES = pull-api.c $(TEST_SRCS)
 range_test_SOURCES = range-test.c $(TEST_SRCS)
 redirect_test_SOURCES = redirect-test.c $(TEST_SRCS)
 requester_test_SOURCES = requester-test.c $(TEST_SRCS)
+resource_test_SOURCES = resource-test.c $(TEST_SRCS)
 server_auth_test_SOURCES = server-auth-test.c $(TEST_SRCS)
 simple_httpd_SOURCES = simple-httpd.c
 simple_proxy_SOURCES = simple-proxy.c
@@ -89,6 +93,9 @@ if HAVE_XMLRPC_EPI_PHP
 XMLRPC_TESTS = xmlrpc-test xmlrpc-server-test
 endif
 
+soup-tests.gresource: soup-tests.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/soup-tests.gresource.xml)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir)  $<
+
 TESTS =                        \
        chunk-test      \
        coding-test     \
@@ -103,6 +110,7 @@ TESTS =                     \
        ntlm-test       \
        redirect-test   \
        requester-test  \
+       resource-test   \
        sniffing-test   \
        socket-test     \
        ssl-test        \
@@ -127,17 +135,20 @@ RESOURCES =                       \
        resources/test.html     \
        resources/text_binary.txt
 
-EXTRA_DIST =                   \
-       htdigest                \
-       htpasswd                \
-       httpd.conf.in           \
-       index.txt               \
-       libsoup.supp            \
-       test-cert.pem           \
-       test-key.pem            \
-       xmlrpc-server.php       \
+EXTRA_DIST =                    \
+       htdigest                 \
+       htpasswd                 \
+       httpd.conf.in            \
+       index.txt                \
+       libsoup.supp             \
+       soup-tests.gresource.xml \
+       test-cert.pem            \
+       test-key.pem             \
+       xmlrpc-server.php        \
        $(RESOURCES)
 
+DISTCLEANFILES = soup-tests.gresource
+
 if MISSING_REGRESSION_TEST_PACKAGES
 check-local: check-TESTS
        @echo ""
diff --git a/tests/resource-test.c b/tests/resource-test.c
new file mode 100644 (file)
index 0000000..449820a
--- /dev/null
@@ -0,0 +1,296 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ */
+
+#include "test-utils.h"
+
+SoupBuffer *index_buffer;
+
+static void
+get_index (void)
+{
+       char *contents;
+       gsize length;
+       GError *error = NULL;
+
+       if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) {
+               g_printerr ("Could not read index.txt: %s\n",
+                           error->message);
+               exit (1);
+       }
+
+       index_buffer = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length);
+}
+
+static void
+register_gresource (void)
+{
+       GResource *resource;
+       GError *error = NULL;
+
+       resource = g_resource_load ("soup-tests.gresource", &error);
+       if (!resource) {
+               g_printerr ("Could not load resource soup-tests.gresource: %s\n",
+                           error->message);
+               exit (1);
+       }
+       g_resources_register (resource);
+       g_resource_unref (resource);
+}
+
+static void
+check_results (GString *body)
+{
+       if (body->len != index_buffer->length) {
+               debug_printf (1, "    body length mismatch: expected %d, got %d\n",
+                             (int)index_buffer->length, (int)body->len);
+               errors++;
+       } else if (memcmp (body->str, index_buffer->data, body->len) != 0) {
+               debug_printf (1, "    body data mismatch\n");
+               errors++;
+       }
+}
+
+typedef struct {
+       GString *body;
+       char buffer[1024];
+       GMainLoop *loop;
+} AsyncRequestData;
+
+static void
+stream_closed (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+       GInputStream *in = G_INPUT_STREAM (source);
+       AsyncRequestData *data = user_data;
+       GError *error = NULL;
+
+       if (!g_input_stream_close_finish (in, result, &error)) {
+               debug_printf (1, "    close failed: %s\n", error->message);
+               g_error_free (error);
+               errors++;
+       }
+       g_main_loop_quit (data->loop);
+       g_object_unref (in);
+}
+
+static void
+test_read_ready (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+       GInputStream *in = G_INPUT_STREAM (source);
+       AsyncRequestData *data = user_data;
+       gssize nread;
+       GError *error = NULL;
+
+       nread = g_input_stream_read_finish (in, result, &error);
+       if (nread == -1) {
+               debug_printf (1, "    g_input_stream_read failed: %s\n",
+                             error->message);
+               g_clear_error (&error);
+               g_input_stream_close (in, NULL, NULL);
+               g_object_unref (in);
+               errors++;
+               return;
+       } else if (nread == 0) {
+               g_input_stream_close_async (in, G_PRIORITY_DEFAULT, NULL,
+                                           stream_closed, data);
+               return;
+       }
+
+       g_string_append_len (data->body, data->buffer, nread);
+       g_input_stream_read_async (in, data->buffer, sizeof (data->buffer),
+                                  G_PRIORITY_DEFAULT, NULL,
+                                  test_read_ready, data);
+}
+
+static void
+async_request_sent (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+       GInputStream *in;
+       AsyncRequestData *data = user_data;
+       GError *error = NULL;
+
+       in = soup_request_send_finish (SOUP_REQUEST (source), result, &error);
+       if (!in) {
+               debug_printf (1, "    soup_request_send_async failed: %s\n",
+                             error->message);
+               g_clear_error (&error);
+               errors++;
+               return;
+       }
+
+       g_input_stream_read_async (in, data->buffer, sizeof (data->buffer),
+                                  G_PRIORITY_DEFAULT, NULL,
+                                  test_read_ready, data);
+}
+
+static void
+do_async_request (SoupRequest *request)
+{
+       AsyncRequestData data;
+
+       data.body = g_string_new (NULL);
+       soup_request_send_async (request, NULL, async_request_sent, &data);
+
+       data.loop = g_main_loop_new (soup_session_get_async_context (soup_request_get_session (request)), TRUE);
+       g_main_loop_run (data.loop);
+       g_main_loop_unref (data.loop);
+
+       check_results (data.body);
+       g_string_free (data.body, TRUE);
+}
+
+static void
+do_sync_request (SoupRequest *request)
+{
+       GInputStream *in;
+       GString *body;
+       char buffer[1024];
+       gssize nread;
+       GError *error = NULL;
+
+       in = soup_request_send (request, NULL, &error);
+       if (!in) {
+               debug_printf (1, "    soup_request_send failed: %s\n",
+                             error->message);
+               g_clear_error (&error);
+               errors++;
+               return;
+       }
+
+       body = g_string_new (NULL);
+       do {
+               nread = g_input_stream_read (in, buffer, sizeof (buffer),
+                                            NULL, &error);
+               if (nread == -1) {
+                       debug_printf (1, "    g_input_stream_read failed: %s\n",
+                                     error->message);
+                       g_clear_error (&error);
+                       errors++;
+                       break;
+               }
+               g_string_append_len (body, buffer, nread);
+       } while (nread > 0);
+
+       if (!g_input_stream_close (in, NULL, &error)) {
+               debug_printf (1, "    g_input_stream_close failed: %s\n",
+                             error->message);
+               g_clear_error (&error);
+               errors++;
+       }
+       g_object_unref (in);
+
+       check_results (body);
+       g_string_free (body, TRUE);
+}
+
+static void
+do_request_file_test (SoupRequester *requester,
+                     gboolean       async)
+{
+       SoupRequest *request;
+       GFile *index;
+       char *uri_string;
+       SoupURI *uri;
+
+       index = g_file_new_for_path (SRCDIR "/index.txt");
+       uri_string = g_file_get_uri (index);
+       g_object_unref (index);
+
+       uri = soup_uri_new (uri_string);
+       g_free (uri_string);
+
+       request = soup_requester_request_uri (requester, uri, NULL);
+       if (async)
+               do_async_request (request);
+       else
+               do_sync_request (request);
+       g_object_unref (request);
+
+       soup_uri_free (uri);
+}
+
+static void
+do_request_data_test (SoupRequester *requester,
+                     gboolean       async)
+{
+       SoupRequest *request;
+       gchar *base64;
+       char *uri_string;
+       SoupURI *uri;
+
+       base64 = g_base64_encode ((const guchar *)index_buffer->data, index_buffer->length);
+       uri_string = g_strdup_printf ("data:text/plain;charset=utf8;base64,%s", base64);
+       g_free (base64);
+
+       uri = soup_uri_new (uri_string);
+       g_free (uri_string);
+
+       request = soup_requester_request_uri (requester, uri, NULL);
+       if (async)
+               do_async_request (request);
+       else
+               do_sync_request (request);
+       g_object_unref (request);
+
+       soup_uri_free (uri);
+}
+
+static void
+do_request_gresource_test (SoupRequester *requester,
+                          gboolean       async)
+{
+       SoupRequest *request;
+       SoupURI *uri;
+
+       uri = soup_uri_new ("resource:///org/gnome/libsoup/tests/index.txt");
+       request = soup_requester_request_uri (requester, uri, NULL);
+       if (async)
+               do_async_request (request);
+       else
+               do_sync_request (request);
+       g_object_unref (request);
+
+       soup_uri_free (uri);
+}
+
+int
+main (int argc, char **argv)
+{
+       SoupSession *session;
+       SoupRequester *requester;
+
+       test_init (argc, argv, NULL);
+
+       get_index ();
+       register_gresource ();
+
+       /* Sync tests */
+       session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+       requester = soup_requester_new ();
+       soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
+       g_object_unref (requester);
+
+       do_request_file_test (requester, FALSE);
+       do_request_data_test (requester, FALSE);
+       do_request_gresource_test (requester, FALSE);
+
+       soup_test_session_abort_unref (session);
+
+       /* Async tests */
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+                                        SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+                                        NULL);
+       requester = soup_requester_new ();
+       soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
+       g_object_unref (requester);
+
+       do_request_file_test (requester, TRUE);
+       do_request_data_test (requester, TRUE);
+       do_request_gresource_test (requester, TRUE);
+
+       soup_test_session_abort_unref (session);
+
+       test_cleanup ();
+       return errors != 0;
+}
diff --git a/tests/soup-tests.gresource.xml b/tests/soup-tests.gresource.xml
new file mode 100644 (file)
index 0000000..969a6bf
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/libsoup/tests">
+    <file>index.txt</file>
+  </gresource>
+</gresources>
\ No newline at end of file