Refactor web cache to circumvent libsoup when needed

At this point libsoup is dependent on a functional network adapter,
which may not be available if no network connection is there or
the connection is faulty. So *if* we have a requested page in the
cache, we load it before libsoup comes into play.

Depending on the WebKitGTK+ version, loading from cache uses an
"alternate" URI so that we don't end up having cache filenames
in the location entry.

Generation of cache filenames is fixed to never include special
characters such as slashes or question marks.

Only non-empty files are actually stored to the cache folder.
This commit is contained in:
Christian Dywan 2009-10-21 23:45:46 +02:00
parent f875144aa3
commit 9040d43632

View file

@ -19,7 +19,7 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#define HAVE_WEBKIT_RESOURCE_REQUEST 0 /* WEBKIT_CHECK_VERSION (1, 1, 14) */ #define HAVE_WEBKIT_RESOURCE_REQUEST WEBKIT_CHECK_VERSION (1, 1, 14)
static gchar* static gchar*
web_cache_get_cached_path (MidoriExtension* extension, web_cache_get_cached_path (MidoriExtension* extension,
@ -29,6 +29,7 @@ web_cache_get_cached_path (MidoriExtension* extension,
gchar* checksum; gchar* checksum;
gchar* folder; gchar* folder;
gchar* sub_path; gchar* sub_path;
gchar* encoded;
gchar* ext; gchar* ext;
gchar* cached_filename; gchar* cached_filename;
gchar* cached_path; gchar* cached_path;
@ -43,14 +44,73 @@ web_cache_get_cached_path (MidoriExtension* extension,
g_mkdir (sub_path, 0700); g_mkdir (sub_path, 0700);
g_free (folder); g_free (folder);
ext = g_strrstr (uri, "."); encoded = soup_uri_encode (uri, "/");
ext = g_strrstr (encoded, ".");
cached_filename = g_strdup_printf ("%s%s", checksum, ext ? ext : ""); cached_filename = g_strdup_printf ("%s%s", checksum, ext ? ext : "");
g_free (encoded);
g_free (checksum); g_free (checksum);
cached_path = g_build_filename (sub_path, cached_filename, NULL); cached_path = g_build_filename (sub_path, cached_filename, NULL);
g_free (cached_filename); g_free (cached_filename);
return cached_path; return cached_path;
} }
static gboolean
web_cache_replace_frame_uri (MidoriExtension* extension,
const gchar* uri,
WebKitWebFrame* web_frame)
{
gchar* filename;
gboolean handled = FALSE;
filename = web_cache_get_cached_path (extension, uri);
/* g_debug ("cache lookup: %s => %s", uri, filename); */
if (g_file_test (filename, G_FILE_TEST_EXISTS))
{
gchar* data;
g_file_get_contents (filename, &data, NULL, NULL);
webkit_web_frame_load_alternate_string (web_frame, data, NULL, uri);
g_free (data);
handled = TRUE;
}
g_free (filename);
return handled;
}
static gboolean
web_cache_navigation_decision_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
WebKitNetworkRequest* request,
WebKitWebNavigationAction* action,
WebKitWebPolicyDecision* decision,
MidoriExtension* extension)
{
const gchar* uri = webkit_network_request_get_uri (request);
if (!(uri && g_str_has_prefix (uri, "http://")))
return FALSE;
return web_cache_replace_frame_uri (extension, uri, web_frame);
}
#if WEBKIT_CHECK_VERSION (1, 1, 6)
static gboolean
web_cache_load_error_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
const gchar* uri,
GError* error,
MidoriExtension* extension)
{
const gchar* provisional;
if (!(uri && g_str_has_prefix (uri, "http://")))
return FALSE;
return web_cache_replace_frame_uri (extension, uri, web_frame);
}
#endif
#if HAVE_WEBKIT_RESOURCE_REQUEST #if HAVE_WEBKIT_RESOURCE_REQUEST
static void static void
web_cache_resource_request_starting_cb (WebKitWebView* web_view, web_cache_resource_request_starting_cb (WebKitWebView* web_view,
@ -67,10 +127,12 @@ web_cache_resource_request_starting_cb (WebKitWebView* web_view,
if (!(uri && g_str_has_prefix (uri, "http://"))) if (!(uri && g_str_has_prefix (uri, "http://")))
return; return;
/* g_debug ("cache lookup: %s => %s", uri, filename); */ if (!(g_strcmp0 (uri, webkit_web_frame_get_uri (web_frame))
&& g_strcmp0 (webkit_web_data_source_get_unreachable_uri (webkit_web_frame_get_data_source (web_frame)), uri)))
/* FIXME: Replacing the main resource of the frame is bad, {
we must either skip it or find a way to redirect internally. */ web_cache_replace_frame_uri (extension, uri, web_frame);
return;
}
filename = web_cache_get_cached_path (extension, uri); filename = web_cache_get_cached_path (extension, uri);
if (g_file_test (filename, G_FILE_TEST_EXISTS)) if (g_file_test (filename, G_FILE_TEST_EXISTS))
@ -79,10 +141,10 @@ web_cache_resource_request_starting_cb (WebKitWebView* web_view,
webkit_network_request_set_uri (request, file_uri); webkit_network_request_set_uri (request, file_uri);
g_free (file_uri); g_free (file_uri);
} }
g_free (filename); g_free (filename);
} }
#else #endif
static void static void
web_cache_mesage_got_headers_cb (SoupMessage* msg, web_cache_mesage_got_headers_cb (SoupMessage* msg,
MidoriExtension* extension) MidoriExtension* extension)
@ -107,7 +169,6 @@ web_cache_mesage_got_headers_cb (SoupMessage* msg,
g_free (filename); g_free (filename);
g_free (uri); g_free (uri);
} }
#endif
static void static void
web_cache_mesage_got_chunk_cb (SoupMessage* msg, web_cache_mesage_got_chunk_cb (SoupMessage* msg,
@ -132,17 +193,18 @@ web_cache_session_request_queued_cb (SoupSession* session,
{ {
gchar* filename = web_cache_get_cached_path (extension, uri); gchar* filename = web_cache_get_cached_path (extension, uri);
/* g_debug ("cache lookup: %s => %s", uri, filename); */ /* g_debug ("cache lookup: %d %s => %s", msg->status_code, uri, filename); */
g_object_set_data (G_OBJECT (msg), "session", session); g_object_set_data (G_OBJECT (msg), "session", session);
#if HAVE_WEBKIT_RESOURCE_REQUEST
if (!g_file_test (filename, G_FILE_TEST_EXISTS)) /* Network is unavailable, so we fallback to cache */
#else if (msg->status_code == SOUP_STATUS_CANT_RESOLVE)
web_cache_mesage_got_headers_cb (msg, extension);
if (g_file_test (filename, G_FILE_TEST_EXISTS)) if (g_file_test (filename, G_FILE_TEST_EXISTS))
g_signal_connect (msg, "got-headers", g_signal_connect (msg, "got-headers",
G_CALLBACK (web_cache_mesage_got_headers_cb), extension); G_CALLBACK (web_cache_mesage_got_headers_cb), extension);
else else
#endif
g_signal_connect (msg, "got-chunk", g_signal_connect (msg, "got-chunk",
G_CALLBACK (web_cache_mesage_got_chunk_cb), extension); G_CALLBACK (web_cache_mesage_got_chunk_cb), extension);
@ -170,6 +232,7 @@ web_cache_session_request_unqueued_cb (SoupSession* session,
#endif #endif
/* FIXME: Only store if this wasn't a cached message already */ /* FIXME: Only store if this wasn't a cached message already */
/* FIXME: Don't store files from the res server */
if (uri && g_str_has_prefix (uri, "http")) if (uri && g_str_has_prefix (uri, "http"))
{ {
SoupMessageHeaders* hdrs = msg->response_headers; SoupMessageHeaders* hdrs = msg->response_headers;
@ -177,22 +240,34 @@ web_cache_session_request_unqueued_cb (SoupSession* session,
/* FIXME: Don't store big files */ /* FIXME: Don't store big files */
/* Only images are cached */ if (mime_type)
if (mime_type && g_str_has_prefix (mime_type, "image/"))
{ {
gchar* filename = web_cache_get_cached_path (extension, uri); gchar* filename = web_cache_get_cached_path (extension, uri);
SoupMessageBody* body = msg->response_body; SoupMessageBody* body = msg->response_body;
SoupBuffer* buffer = NULL; SoupBuffer* buffer = NULL;
/* We fed the buffer manually before, so this actually works. */ /* We fed the buffer manually before, so this actually works. */
if (!soup_message_body_get_accumulate (body))
{
soup_message_body_set_accumulate (body, TRUE); soup_message_body_set_accumulate (body, TRUE);
buffer = soup_message_body_flatten (body); buffer = soup_message_body_flatten (body);
}
/* g_debug ("cache store: %s => %s", uri, filename); */
/* FIXME: Update sensibly */ /* FIXME: Update sensibly */
if (!g_file_test (filename, G_FILE_TEST_EXISTS)) if (!g_file_test (filename, G_FILE_TEST_EXISTS))
g_file_set_contents (filename, body->data, body->length, NULL); if (body->length)
{
/* g_debug ("cache store: %s => %s (%d)", uri, filename, body->length); */
GError* error = NULL;
g_file_set_contents (filename, body->data, body->length, &error);
if (error)
{
g_printf ("%s\n", error->message);
g_error_free (error);
}
}
/* else
g_debug ("cache skip empty"); */
if (buffer) if (buffer)
soup_buffer_free (buffer); soup_buffer_free (buffer);
@ -210,6 +285,12 @@ web_cache_add_tab_cb (MidoriBrowser* browser,
MidoriExtension* extension) MidoriExtension* extension)
{ {
GtkWidget* web_view = gtk_bin_get_child (GTK_BIN (view)); GtkWidget* web_view = gtk_bin_get_child (GTK_BIN (view));
g_signal_connect (web_view, "navigation-policy-decision-requested",
G_CALLBACK (web_cache_navigation_decision_cb), extension);
#if WEBKIT_CHECK_VERSION (1, 1, 6)
g_signal_connect (web_view, "load-error",
G_CALLBACK (web_cache_load_error_cb), extension);
#endif
#if HAVE_WEBKIT_RESOURCE_REQUEST #if HAVE_WEBKIT_RESOURCE_REQUEST
g_signal_connect (web_view, "resource-request-starting", g_signal_connect (web_view, "resource-request-starting",
G_CALLBACK (web_cache_resource_request_starting_cb), extension); G_CALLBACK (web_cache_resource_request_starting_cb), extension);
@ -253,10 +334,9 @@ web_cache_deactivate_tabs (MidoriView* view,
#if HAVE_WEBKIT_RESOURCE_REQUEST #if HAVE_WEBKIT_RESOURCE_REQUEST
g_signal_handlers_disconnect_by_func ( g_signal_handlers_disconnect_by_func (
web_view, web_cache_resource_request_starting_cb, extension); web_view, web_cache_resource_request_starting_cb, extension);
#else #endif
g_signal_handlers_disconnect_by_func ( g_signal_handlers_disconnect_by_func (
webkit_get_default_session (), web_cache_session_request_queued_cb, extension); webkit_get_default_session (), web_cache_session_request_queued_cb, extension);
#endif
} }
static void static void
@ -281,10 +361,8 @@ web_cache_activate_cb (MidoriExtension* extension,
guint i; guint i;
SoupSession* session = webkit_get_default_session (); SoupSession* session = webkit_get_default_session ();
#if !HAVE_WEBKIT_RESOURCE_REQUEST
g_signal_connect (session, "request-queued", g_signal_connect (session, "request-queued",
G_CALLBACK (web_cache_session_request_queued_cb), extension); G_CALLBACK (web_cache_session_request_queued_cb), extension);
#endif
g_signal_connect (session, "request-unqueued", g_signal_connect (session, "request-unqueued",
G_CALLBACK (web_cache_session_request_unqueued_cb), extension); G_CALLBACK (web_cache_session_request_unqueued_cb), extension);