2010-10-13 Sergio Villar Senin <svillar@igalia.com>
authormrobinson@webkit.org <mrobinson@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 Oct 2010 00:32:37 +0000 (00:32 +0000)
committermrobinson@webkit.org <mrobinson@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 Oct 2010 00:32:37 +0000 (00:32 +0000)
        Reviewed by Martin Robinson.

        [GTK] Add HTTP caching support
        https://bugs.webkit.org/show_bug.cgi?id=44261

        This patch adds HTTP caching support to libsoup networking backend.
        This code will eventually be part of libsoup, but has been temporarily
        imported into the WebCore tree for testing purposes. All libsoup code
        is located in WebCore/platform/network/soup/cache. The webkit prefix
        was added to all the symbols to prevent problems when this lands in
        libsoup. Some external API was also added under webkit/ directory and
        will be used by library clients to enable HTTP caching.

        There are two parts to this imported code, the first one is the code
        under WebCore/platform/network/soup/cache/ that contains all the
        SoupURILoader code that supports the development of the cache. The
        second part is the HTTP cache implementation located in
        WebCore/platform/network/soup/cache/webkit/.

        * GNUmakefile.am:
        * platform/network/soup/cache/soup-directory-input-stream.c: Added.
        (webkit_soup_directory_input_stream_parse_info):
        (webkit_soup_directory_input_stream_read_next_file):
        (webkit_soup_directory_input_stream_read):
        (webkit_soup_directory_input_stream_close):
        (webkit_soup_directory_input_stream_class_init):
        (webkit_soup_directory_input_stream_init):
        (webkit_soup_directory_input_stream_new):
        * platform/network/soup/cache/soup-directory-input-stream.h: Added.
        * platform/network/soup/cache/soup-http-input-stream.c: Added.
        (webkit_soup_http_input_stream_finalize):
        (webkit_soup_http_input_stream_class_init):
        (webkit_soup_http_input_stream_seekable_iface_init):
        (webkit_soup_http_input_stream_init):
        (webkit_soup_http_input_stream_queue_message):
        (webkit_soup_http_input_stream_new):
        (webkit_soup_http_input_stream_got_headers):
        (webkit_soup_http_input_stream_got_chunk):
        (webkit_soup_http_input_stream_finished):
        (webkit_soup_http_input_stream_cancelled):
        (webkit_soup_http_input_stream_prepare_for_io):
        (webkit_soup_http_input_stream_done_io):
        (set_error_if_http_failed):
        (read_from_leftover):
        (webkit_soup_http_input_stream_send_internal):
        (send_sync_finished):
        (webkit_soup_http_input_stream_send):
        (webkit_soup_http_input_stream_read):
        (webkit_soup_http_input_stream_close):
        (wrapper_callback):
        (send_async_thread):
        (webkit_soup_http_input_stream_send_async_in_thread):
        (send_async_finished):
        (webkit_soup_http_input_stream_send_async_internal):
        (webkit_soup_http_input_stream_send_async):
        (webkit_soup_http_input_stream_send_finish):
        (read_async_done):
        (webkit_soup_http_input_stream_read_async):
        (webkit_soup_http_input_stream_read_finish):
        (webkit_soup_http_input_stream_close_async):
        (webkit_soup_http_input_stream_close_finish):
        (webkit_soup_http_input_stream_tell):
        (webkit_soup_http_input_stream_can_seek):
        (webkit_soup_http_input_stream_seek):
        (webkit_soup_http_input_stream_can_truncate):
        (webkit_soup_http_input_stream_truncate):
        (webkit_soup_http_input_stream_get_message):
        * platform/network/soup/cache/soup-http-input-stream.h: Added.
        * platform/network/soup/cache/soup-request-data.c: Added.
        (webkit_soup_request_data_init):
        (webkit_soup_request_data_finalize):
        (webkit_soup_request_data_check_uri):
        (uri_decoded_copy):
        (webkit_soup_request_data_send):
        (webkit_soup_request_data_get_content_length):
        (webkit_soup_request_data_get_content_type):
        (webkit_soup_request_data_class_init):
        * platform/network/soup/cache/soup-request-data.h: Added.
        * platform/network/soup/cache/soup-request-file.c: Added.
        (webkit_soup_request_file_get_file):
        (webkit_soup_request_file_init):
        (webkit_soup_request_file_finalize):
        (webkit_soup_request_file_check_uri):
        (webkit_soup_request_file_ftp_main_loop_quit):
        (webkit_soup_request_file_ensure_file_ftp):
        (webkit_soup_request_file_ensure_file):
        (webkit_soup_request_file_send):
        (webkit_soup_request_file_send_async_thread):
        (webkit_soup_request_file_send_async):
        (webkit_soup_request_file_send_finish):
        (webkit_soup_request_file_get_content_length):
        (webkit_soup_request_file_get_content_type):
        (webkit_soup_request_file_class_init):
        * platform/network/soup/cache/soup-request-file.h: Added.
        * platform/network/soup/cache/soup-request-http.c: Added.
        (webkit_soup_request_http_get_message):
        (webkit_soup_request_http_init):
        (webkit_soup_request_http_check_uri):
        (webkit_soup_request_http_finalize):
        (webkit_soup_request_http_send):
        (sent_async):
        (conditional_get_ready_cb):
        (send_async_cb):
        (webkit_soup_request_http_send_async):
        (webkit_soup_request_http_send_finish):
        (webkit_soup_request_http_get_content_length):
        (webkit_soup_request_http_get_content_type):
        (webkit_soup_request_http_class_init):
        * platform/network/soup/cache/soup-request-http.h: Added.
        * platform/network/soup/cache/soup-request.c: Added.
        (webkit_soup_request_init):
        (webkit_soup_request_finalize):
        (webkit_soup_request_set_property):
        (webkit_soup_request_get_property):
        (webkit_soup_request_initable_init):
        (webkit_soup_request_default_check_uri):
        (webkit_soup_request_default_send_async):
        (webkit_soup_request_default_send_finish):
        (webkit_soup_request_send):
        (webkit_soup_request_send_async):
        (webkit_soup_request_send_finish):
        (webkit_soup_request_class_init):
        (webkit_soup_request_initable_interface_init):
        (webkit_soup_request_get_uri):
        (webkit_soup_request_get_session):
        (webkit_soup_request_get_content_length):
        (webkit_soup_request_get_content_type):
        * platform/network/soup/cache/soup-request.h: Added.
        * platform/network/soup/cache/soup-requester.c: Added.
        (webkit_soup_requester_init):
        (finalize):
        (webkit_soup_requester_class_init):
        (init_request_types):
        (webkit_soup_requester_new):
        (webkit_soup_requester_request):
        (webkit_soup_requester_request_uri):
        (webkit_soup_scheme_is_valid):
        (webkit_soup_requester_add_protocol):
        (webkit_soup_requester_remove_protocol):
        (webkit_soup_error_quark):
        * platform/network/soup/cache/soup-requester.h: Added.
        * platform/network/soup/cache/webkit/soup-cache-private.h: Added.
        * platform/network/soup/cache/webkit/soup-cache.c: Added.
        (get_cacheability):
        (webkit_soup_cache_entry_free):
        (copy_headers):
        (update_headers):
        (webkit_soup_cache_entry_get_current_age):
        (webkit_soup_cache_entry_is_fresh_enough):
        (webkit_soup_message_get_cache_key):
        (webkit_soup_cache_entry_set_freshness):
        (webkit_soup_cache_entry_new):
        (webkit_soup_cache_writing_fixture_free):
        (close_ready_cb):
        (write_ready_cb):
        (msg_got_chunk_cb):
        (msg_got_body_cb):
        (webkit_soup_cache_entry_delete):
        (lru_compare_func):
        (cache_accepts_entries_of_size):
        (make_room_for_new_entry):
        (webkit_soup_cache_entry_insert_by_key):
        (msg_restarted_cb):
        (append_to_ready_cb):
        (msg_got_headers_cb):
        (webkit_soup_cache_send_response):
        (request_started):
        (attach):
        (webkit_soup_cache_session_feature_init):
        (webkit_soup_cache_init):
        (webkit_soup_cache_finalize):
        (webkit_soup_cache_set_property):
        (webkit_soup_cache_get_property):
        (webkit_soup_cache_constructed):
        (webkit_soup_cache_type_get_type):
        (webkit_soup_cache_class_init):
        (webkit_soup_cache_new):
        (webkit_soup_cache_has_response):
        (webkit_soup_cache_get_cacheability):
        (force_flush_timeout):
        (webkit_soup_cache_flush):
        (remove_cache_item):
        (webkit_soup_cache_clear):
        (webkit_soup_cache_generate_conditional_request):
        (pack_entry):
        (webkit_soup_cache_dump):
        (webkit_soup_cache_load):
        (webkit_soup_cache_set_max_size):
        (webkit_soup_cache_get_max_size):
        * platform/network/soup/cache/webkit/soup-cache.h: Added.
2010-10-13  Sergio Villar Senin  <svillar@igalia.com>

        Reviewed by Martin Robinson.

        [GTK] Add HTTP caching support
        https://bugs.webkit.org/show_bug.cgi?id=44261

        Bump the required version of GLib. The serialization/deserialization
        code of libsoup caching code uses GVariant, which was added to GLib in
        version 2.24.

        * GNUmakefile.am: Add paths for soup HTTP cache code.
        * autotools/webkit.m4: Increase required glib version.
2010-10-13  Sergio Villar Senin  <svillar@igalia.com>

        Reviewed by Martin Robinson.

        [GTK] Add HTTP caching support
        https://bugs.webkit.org/show_bug.cgi?id=44261

        Add include paths for the new soup HTTP cache code.

        * GNUmakefile.am: Add paths for the new soup HTTP cache code.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@69718 268f45cc-cd09-0410-ab3c-d52691b4dbfc

24 files changed:
ChangeLog
GNUmakefile.am
WebCore/ChangeLog
WebCore/GNUmakefile.am
WebCore/platform/network/soup/cache/soup-directory-input-stream.c [new file with mode: 0644]
WebCore/platform/network/soup/cache/soup-directory-input-stream.h [new file with mode: 0644]
WebCore/platform/network/soup/cache/soup-http-input-stream.c [new file with mode: 0644]
WebCore/platform/network/soup/cache/soup-http-input-stream.h [new file with mode: 0644]
WebCore/platform/network/soup/cache/soup-request-data.c [new file with mode: 0644]
WebCore/platform/network/soup/cache/soup-request-data.h [new file with mode: 0644]
WebCore/platform/network/soup/cache/soup-request-file.c [new file with mode: 0644]
WebCore/platform/network/soup/cache/soup-request-file.h [new file with mode: 0644]
WebCore/platform/network/soup/cache/soup-request-http.c [new file with mode: 0644]
WebCore/platform/network/soup/cache/soup-request-http.h [new file with mode: 0644]
WebCore/platform/network/soup/cache/soup-request.c [new file with mode: 0644]
WebCore/platform/network/soup/cache/soup-request.h [new file with mode: 0644]
WebCore/platform/network/soup/cache/soup-requester.c [new file with mode: 0644]
WebCore/platform/network/soup/cache/soup-requester.h [new file with mode: 0644]
WebCore/platform/network/soup/cache/webkit/soup-cache-private.h [new file with mode: 0644]
WebCore/platform/network/soup/cache/webkit/soup-cache.c [new file with mode: 0644]
WebCore/platform/network/soup/cache/webkit/soup-cache.h [new file with mode: 0644]
WebKitTools/ChangeLog
WebKitTools/GNUmakefile.am
autotools/webkit.m4

index e0a0606..b3df238 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2010-10-13  Sergio Villar Senin  <svillar@igalia.com>
+
+        Reviewed by Martin Robinson.
+
+        [GTK] Add HTTP caching support
+        https://bugs.webkit.org/show_bug.cgi?id=44261
+
+        Bump the required version of GLib. The serialization/deserialization
+        code of libsoup caching code uses GVariant, which was added to GLib in
+        version 2.24.
+
+        * GNUmakefile.am: Add paths for soup HTTP cache code.
+        * autotools/webkit.m4: Increase required glib version.
+
 2010-10-13  Xan Lopez  <xlopez@igalia.com>
 
         Reviewed by Gustavo Noronha.
index a1bf895..1c64f5c 100644 (file)
@@ -308,28 +308,29 @@ global_cppflags += \
 endif
 
 webkitgtk_static_h_api += \
+       $(srcdir)/WebCore/platform/network/soup/cache/webkit/soup-cache.h \
        $(srcdir)/WebKit/gtk/webkit/webkit.h \
        $(srcdir)/WebKit/gtk/webkit/webkitdefines.h \
        $(srcdir)/WebKit/gtk/webkit/webkitdownload.h \
        $(srcdir)/WebKit/gtk/webkit/webkiterror.h \
+       $(srcdir)/WebKit/gtk/webkit/webkitgeolocationpolicydecision.h \
        $(srcdir)/WebKit/gtk/webkit/webkithittestresult.h \
        $(srcdir)/WebKit/gtk/webkit/webkitnetworkrequest.h \
        $(srcdir)/WebKit/gtk/webkit/webkitnetworkresponse.h \
+       $(srcdir)/WebKit/gtk/webkit/webkitsecurityorigin.h \
        $(srcdir)/WebKit/gtk/webkit/webkitsoupauthdialog.h \
        $(srcdir)/WebKit/gtk/webkit/webkitwebbackforwardlist.h \
+       $(srcdir)/WebKit/gtk/webkit/webkitwebdatabase.h \
        $(srcdir)/WebKit/gtk/webkit/webkitwebdatasource.h \
        $(srcdir)/WebKit/gtk/webkit/webkitwebframe.h \
        $(srcdir)/WebKit/gtk/webkit/webkitwebhistoryitem.h \
        $(srcdir)/WebKit/gtk/webkit/webkitwebinspector.h \
        $(srcdir)/WebKit/gtk/webkit/webkitwebnavigationaction.h \
        $(srcdir)/WebKit/gtk/webkit/webkitwebpolicydecision.h \
-       $(srcdir)/WebKit/gtk/webkit/webkitgeolocationpolicydecision.h \
        $(srcdir)/WebKit/gtk/webkit/webkitwebresource.h \
        $(srcdir)/WebKit/gtk/webkit/webkitwebsettings.h \
        $(srcdir)/WebKit/gtk/webkit/webkitwebwindowfeatures.h \
-       $(srcdir)/WebKit/gtk/webkit/webkitwebview.h \
-       $(srcdir)/WebKit/gtk/webkit/webkitwebdatabase.h \
-       $(srcdir)/WebKit/gtk/webkit/webkitsecurityorigin.h
+       $(srcdir)/WebKit/gtk/webkit/webkitwebview.h
 
 webkitgtk_built_h_api += \
        WebKit/gtk/webkit/webkitversion.h
@@ -557,6 +558,7 @@ noinst_PROGRAMS += $(TEST_PROGS)
 webkit_tests_cflags = \
        -fno-strict-aliasing \
        -I$(srcdir)/JavaScriptCore/ForwardingHeaders \
+       -I$(srcdir)/WebCore/platform/network/soup/cache \
        -I$(srcdir)/WebKit/gtk \
        -I$(top_builddir)/WebKit/gtk \
        -I$(top_builddir)/DerivedSources \
index f51a4b5..f638ff1 100644 (file)
@@ -1,3 +1,196 @@
+2010-10-13  Sergio Villar Senin  <svillar@igalia.com>
+
+        Reviewed by Martin Robinson.
+
+        [GTK] Add HTTP caching support
+        https://bugs.webkit.org/show_bug.cgi?id=44261
+
+        This patch adds HTTP caching support to libsoup networking backend.
+        This code will eventually be part of libsoup, but has been temporarily
+        imported into the WebCore tree for testing purposes. All libsoup code
+        is located in WebCore/platform/network/soup/cache. The webkit prefix
+        was added to all the symbols to prevent problems when this lands in
+        libsoup. Some external API was also added under webkit/ directory and
+        will be used by library clients to enable HTTP caching.
+
+        There are two parts to this imported code, the first one is the code
+        under WebCore/platform/network/soup/cache/ that contains all the
+        SoupURILoader code that supports the development of the cache. The
+        second part is the HTTP cache implementation located in
+        WebCore/platform/network/soup/cache/webkit/.
+
+        * GNUmakefile.am:
+        * platform/network/soup/cache/soup-directory-input-stream.c: Added.
+        (webkit_soup_directory_input_stream_parse_info):
+        (webkit_soup_directory_input_stream_read_next_file):
+        (webkit_soup_directory_input_stream_read):
+        (webkit_soup_directory_input_stream_close):
+        (webkit_soup_directory_input_stream_class_init):
+        (webkit_soup_directory_input_stream_init):
+        (webkit_soup_directory_input_stream_new):
+        * platform/network/soup/cache/soup-directory-input-stream.h: Added.
+        * platform/network/soup/cache/soup-http-input-stream.c: Added.
+        (webkit_soup_http_input_stream_finalize):
+        (webkit_soup_http_input_stream_class_init):
+        (webkit_soup_http_input_stream_seekable_iface_init):
+        (webkit_soup_http_input_stream_init):
+        (webkit_soup_http_input_stream_queue_message):
+        (webkit_soup_http_input_stream_new):
+        (webkit_soup_http_input_stream_got_headers):
+        (webkit_soup_http_input_stream_got_chunk):
+        (webkit_soup_http_input_stream_finished):
+        (webkit_soup_http_input_stream_cancelled):
+        (webkit_soup_http_input_stream_prepare_for_io):
+        (webkit_soup_http_input_stream_done_io):
+        (set_error_if_http_failed):
+        (read_from_leftover):
+        (webkit_soup_http_input_stream_send_internal):
+        (send_sync_finished):
+        (webkit_soup_http_input_stream_send):
+        (webkit_soup_http_input_stream_read):
+        (webkit_soup_http_input_stream_close):
+        (wrapper_callback):
+        (send_async_thread):
+        (webkit_soup_http_input_stream_send_async_in_thread):
+        (send_async_finished):
+        (webkit_soup_http_input_stream_send_async_internal):
+        (webkit_soup_http_input_stream_send_async):
+        (webkit_soup_http_input_stream_send_finish):
+        (read_async_done):
+        (webkit_soup_http_input_stream_read_async):
+        (webkit_soup_http_input_stream_read_finish):
+        (webkit_soup_http_input_stream_close_async):
+        (webkit_soup_http_input_stream_close_finish):
+        (webkit_soup_http_input_stream_tell):
+        (webkit_soup_http_input_stream_can_seek):
+        (webkit_soup_http_input_stream_seek):
+        (webkit_soup_http_input_stream_can_truncate):
+        (webkit_soup_http_input_stream_truncate):
+        (webkit_soup_http_input_stream_get_message):
+        * platform/network/soup/cache/soup-http-input-stream.h: Added.
+        * platform/network/soup/cache/soup-request-data.c: Added.
+        (webkit_soup_request_data_init):
+        (webkit_soup_request_data_finalize):
+        (webkit_soup_request_data_check_uri):
+        (uri_decoded_copy):
+        (webkit_soup_request_data_send):
+        (webkit_soup_request_data_get_content_length):
+        (webkit_soup_request_data_get_content_type):
+        (webkit_soup_request_data_class_init):
+        * platform/network/soup/cache/soup-request-data.h: Added.
+        * platform/network/soup/cache/soup-request-file.c: Added.
+        (webkit_soup_request_file_get_file):
+        (webkit_soup_request_file_init):
+        (webkit_soup_request_file_finalize):
+        (webkit_soup_request_file_check_uri):
+        (webkit_soup_request_file_ftp_main_loop_quit):
+        (webkit_soup_request_file_ensure_file_ftp):
+        (webkit_soup_request_file_ensure_file):
+        (webkit_soup_request_file_send):
+        (webkit_soup_request_file_send_async_thread):
+        (webkit_soup_request_file_send_async):
+        (webkit_soup_request_file_send_finish):
+        (webkit_soup_request_file_get_content_length):
+        (webkit_soup_request_file_get_content_type):
+        (webkit_soup_request_file_class_init):
+        * platform/network/soup/cache/soup-request-file.h: Added.
+        * platform/network/soup/cache/soup-request-http.c: Added.
+        (webkit_soup_request_http_get_message):
+        (webkit_soup_request_http_init):
+        (webkit_soup_request_http_check_uri):
+        (webkit_soup_request_http_finalize):
+        (webkit_soup_request_http_send):
+        (sent_async):
+        (conditional_get_ready_cb):
+        (send_async_cb):
+        (webkit_soup_request_http_send_async):
+        (webkit_soup_request_http_send_finish):
+        (webkit_soup_request_http_get_content_length):
+        (webkit_soup_request_http_get_content_type):
+        (webkit_soup_request_http_class_init):
+        * platform/network/soup/cache/soup-request-http.h: Added.
+        * platform/network/soup/cache/soup-request.c: Added.
+        (webkit_soup_request_init):
+        (webkit_soup_request_finalize):
+        (webkit_soup_request_set_property):
+        (webkit_soup_request_get_property):
+        (webkit_soup_request_initable_init):
+        (webkit_soup_request_default_check_uri):
+        (webkit_soup_request_default_send_async):
+        (webkit_soup_request_default_send_finish):
+        (webkit_soup_request_send):
+        (webkit_soup_request_send_async):
+        (webkit_soup_request_send_finish):
+        (webkit_soup_request_class_init):
+        (webkit_soup_request_initable_interface_init):
+        (webkit_soup_request_get_uri):
+        (webkit_soup_request_get_session):
+        (webkit_soup_request_get_content_length):
+        (webkit_soup_request_get_content_type):
+        * platform/network/soup/cache/soup-request.h: Added.
+        * platform/network/soup/cache/soup-requester.c: Added.
+        (webkit_soup_requester_init):
+        (finalize):
+        (webkit_soup_requester_class_init):
+        (init_request_types):
+        (webkit_soup_requester_new):
+        (webkit_soup_requester_request):
+        (webkit_soup_requester_request_uri):
+        (webkit_soup_scheme_is_valid):
+        (webkit_soup_requester_add_protocol):
+        (webkit_soup_requester_remove_protocol):
+        (webkit_soup_error_quark):
+        * platform/network/soup/cache/soup-requester.h: Added.
+        * platform/network/soup/cache/webkit/soup-cache-private.h: Added.
+        * platform/network/soup/cache/webkit/soup-cache.c: Added.
+        (get_cacheability):
+        (webkit_soup_cache_entry_free):
+        (copy_headers):
+        (update_headers):
+        (webkit_soup_cache_entry_get_current_age):
+        (webkit_soup_cache_entry_is_fresh_enough):
+        (webkit_soup_message_get_cache_key):
+        (webkit_soup_cache_entry_set_freshness):
+        (webkit_soup_cache_entry_new):
+        (webkit_soup_cache_writing_fixture_free):
+        (close_ready_cb):
+        (write_ready_cb):
+        (msg_got_chunk_cb):
+        (msg_got_body_cb):
+        (webkit_soup_cache_entry_delete):
+        (lru_compare_func):
+        (cache_accepts_entries_of_size):
+        (make_room_for_new_entry):
+        (webkit_soup_cache_entry_insert_by_key):
+        (msg_restarted_cb):
+        (append_to_ready_cb):
+        (msg_got_headers_cb):
+        (webkit_soup_cache_send_response):
+        (request_started):
+        (attach):
+        (webkit_soup_cache_session_feature_init):
+        (webkit_soup_cache_init):
+        (webkit_soup_cache_finalize):
+        (webkit_soup_cache_set_property):
+        (webkit_soup_cache_get_property):
+        (webkit_soup_cache_constructed):
+        (webkit_soup_cache_type_get_type):
+        (webkit_soup_cache_class_init):
+        (webkit_soup_cache_new):
+        (webkit_soup_cache_has_response):
+        (webkit_soup_cache_get_cacheability):
+        (force_flush_timeout):
+        (webkit_soup_cache_flush):
+        (remove_cache_item):
+        (webkit_soup_cache_clear):
+        (webkit_soup_cache_generate_conditional_request):
+        (pack_entry):
+        (webkit_soup_cache_dump):
+        (webkit_soup_cache_load):
+        (webkit_soup_cache_set_max_size):
+        (webkit_soup_cache_get_max_size):
+        * platform/network/soup/cache/webkit/soup-cache.h: Added.
+
 2010-10-13  Gavin Barraclough  <barraclough@apple.com>
 
         Chromium build fix.
index dc6e548..d86f89f 100644 (file)
@@ -78,7 +78,9 @@ webcoregtk_cppflags += \
        -I$(srcdir)/WebCore/platform/graphics/gstreamer \
        -I$(srcdir)/WebCore/platform/graphics/gtk \
        -I$(srcdir)/WebCore/platform/gtk \
-       -I$(srcdir)/WebCore/platform/network/soup
+       -I$(srcdir)/WebCore/platform/network/soup \
+       -I$(srcdir)/WebCore/platform/network/soup/cache \
+       -I$(srcdir)/WebCore/platform/network/soup/cache/webkit
 
 webcore_built_nosources += \
        DerivedSources/WebCore/tokenizer.cpp
@@ -3580,6 +3582,22 @@ webcoregtk_sources += \
        WebCore/platform/network/soup/SocketStreamError.h \
        WebCore/platform/network/soup/SocketStreamHandle.h \
        WebCore/platform/network/soup/SocketStreamHandleSoup.cpp \
+       WebCore/platform/network/soup/cache/soup-directory-input-stream.c \
+       WebCore/platform/network/soup/cache/soup-directory-input-stream.h \
+       WebCore/platform/network/soup/cache/soup-http-input-stream.c \
+       WebCore/platform/network/soup/cache/soup-http-input-stream.h \
+       WebCore/platform/network/soup/cache/soup-request-data.c \
+       WebCore/platform/network/soup/cache/soup-request-data.h \
+       WebCore/platform/network/soup/cache/soup-request-file.c \
+       WebCore/platform/network/soup/cache/soup-request-file.h \
+       WebCore/platform/network/soup/cache/soup-request-http.c \
+       WebCore/platform/network/soup/cache/soup-request-http.h \
+       WebCore/platform/network/soup/cache/soup-request.c \
+       WebCore/platform/network/soup/cache/soup-request.h \
+       WebCore/platform/network/soup/cache/soup-requester.c \
+       WebCore/platform/network/soup/cache/soup-requester.h \
+       WebCore/platform/network/soup/cache/webkit/soup-cache.c \
+       WebCore/platform/network/soup/cache/webkit/soup-cache.h \
        WebCore/plugins/gtk/PluginDataGtk.cpp \
        WebCore/plugins/gtk/PluginPackageGtk.cpp \
        WebCore/plugins/gtk/PluginViewGtk.cpp
diff --git a/WebCore/platform/network/soup/cache/soup-directory-input-stream.c b/WebCore/platform/network/soup/cache/soup-directory-input-stream.c
new file mode 100644 (file)
index 0000000..bf16dbb
--- /dev/null
@@ -0,0 +1,200 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-directory-input-stream.h"
+
+#include <libsoup/soup.h>
+#include <stdio.h>
+#include <string.h>
+
+#define INIT_STRING "<html><head><title>OMG!</title></head><body><table>"
+#define EXIT_STRING "</table></html>"
+
+G_DEFINE_TYPE (WebKitSoupDirectoryInputStream, webkit_soup_directory_input_stream, G_TYPE_INPUT_STREAM)
+
+static SoupBuffer *
+webkit_soup_directory_input_stream_parse_info (WebKitSoupDirectoryInputStream * stream,
+                                              GFileInfo * info)
+{
+       SoupBuffer *buffer;
+       GString *string;
+       const char *s;
+       char *escaped, *path, *xml_string;
+
+       if (!g_file_info_get_name (info))
+               return NULL;
+
+       s = g_file_info_get_display_name (info);
+       if (!s) {
+               s = g_file_info_get_name (info);
+               /* FIXME: convert somehow? */
+               if (!g_utf8_validate (s, -1, NULL))
+                       return NULL;
+       }
+       string = g_string_new ("<tr>");
+
+       xml_string = g_markup_escape_text (s, -1);
+       escaped = g_uri_escape_string (g_file_info_get_name (info), NULL, FALSE);
+       path = g_strconcat (stream->uri, "/", escaped, NULL);
+       g_free (escaped);
+       g_string_append_printf (string, "<td><a href=\"%s\">%s</a></td>", path, xml_string);
+       g_free (path);
+       g_free (xml_string);
+       g_string_append (string, "</tr>");
+
+       buffer = soup_buffer_new (SOUP_MEMORY_TAKE, string->str, string->len);
+       g_string_free (string, FALSE);
+
+       return buffer;
+}
+
+static SoupBuffer *
+webkit_soup_directory_input_stream_read_next_file (WebKitSoupDirectoryInputStream  *stream,
+                                                  GCancellable              *cancellable,
+                                                  GError                   **error)
+{
+       GFileInfo *info;
+       SoupBuffer *buffer;
+       GError *err = NULL;
+
+       do {
+               info = g_file_enumerator_next_file (stream->enumerator, cancellable, &err);
+               if (info == NULL) {
+                       if (err) {
+                               g_propagate_error (error, err);
+                               return NULL;
+                       } else if (!stream->done) {
+                               stream->done = TRUE;
+                               return soup_buffer_new (SOUP_MEMORY_STATIC,
+                                                       EXIT_STRING,
+                                                       sizeof (EXIT_STRING));
+                       } else {
+                               return NULL;
+                       }
+               }
+
+               buffer = webkit_soup_directory_input_stream_parse_info (stream, info);
+       } while (buffer == NULL);
+
+       return buffer;
+}
+
+static gssize
+webkit_soup_directory_input_stream_read (GInputStream  *input,
+                                        void          *buffer,
+                                        gsize count,
+                                        GCancellable  *cancellable,
+                                        GError       **error)
+{
+       WebKitSoupDirectoryInputStream *stream = WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (input);
+       gssize total, size;
+
+       for (total = 0; total < count; total += size) {
+               if (stream->buffer == NULL) {
+                       stream->buffer = webkit_soup_directory_input_stream_read_next_file (stream, cancellable, error);
+                       if (stream->buffer == NULL) {
+                               /* FIXME: Is this correct or should we forward the error? */
+                               if (total)
+                                       g_clear_error (error);
+                               return total;
+                       }
+               }
+
+               size = MIN (stream->buffer->length, count - total);
+               memcpy ((char *)buffer + total, stream->buffer->data, size);
+               if (size == stream->buffer->length) {
+                       soup_buffer_free (stream->buffer);
+                       stream->buffer = NULL;
+               } else {
+                       SoupBuffer *sub = soup_buffer_new_subbuffer (stream->buffer,
+                                                                    size,
+                                                                    stream->buffer->length - size);
+                       soup_buffer_free (stream->buffer);
+                       stream->buffer = sub;
+               }
+       }
+
+       return total;
+}
+
+static gboolean
+webkit_soup_directory_input_stream_close (GInputStream  *input,
+                                         GCancellable  *cancellable,
+                                         GError       **error)
+{
+       WebKitSoupDirectoryInputStream *stream = WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (input);
+       gboolean result;
+
+       if (stream->buffer) {
+               soup_buffer_free (stream->buffer);
+               stream->buffer = NULL;
+       }
+
+       result = g_file_enumerator_close (stream->enumerator,
+                                         cancellable,
+                                         error);
+       g_object_unref (stream->enumerator);
+       stream->enumerator = NULL;
+
+       g_free (stream->uri);
+       stream->uri = NULL;
+
+       return result;
+}
+
+static void
+webkit_soup_directory_input_stream_class_init (WebKitSoupDirectoryInputStreamClass *stream_class)
+{
+       GInputStreamClass *inputstream_class = G_INPUT_STREAM_CLASS (stream_class);
+
+       inputstream_class->read_fn = webkit_soup_directory_input_stream_read;
+       inputstream_class->close_fn = webkit_soup_directory_input_stream_close;
+}
+
+static void
+webkit_soup_directory_input_stream_init (WebKitSoupDirectoryInputStream *stream)
+{
+       stream->buffer = soup_buffer_new (SOUP_MEMORY_STATIC,
+                                         INIT_STRING,
+                                         sizeof (INIT_STRING));
+}
+
+GInputStream *
+webkit_soup_directory_input_stream_new (GFileEnumerator *enumerator,
+                                       SoupURI         *uri)
+{
+       GInputStream *stream;
+
+       g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL);
+       g_return_val_if_fail (uri != NULL, NULL);
+
+       stream = g_object_new (WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, NULL);
+
+       WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (stream)->enumerator = g_object_ref (enumerator);
+       WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (stream)->uri = soup_uri_to_string (uri, FALSE);
+
+       return stream;
+}
+
diff --git a/WebCore/platform/network/soup/cache/soup-directory-input-stream.h b/WebCore/platform/network/soup/cache/soup-directory-input-stream.h
new file mode 100644 (file)
index 0000000..0c5b0be
--- /dev/null
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_H
+#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_H 1
+
+#include <gio/gio.h>
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-message-body.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM            (webkit_soup_directory_input_stream_get_type ())
+#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, WebKitSoupDirectoryInputStream))
+#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, WebKitSoupDirectoryInputStreamClass))
+#define WEBKIT_IS_SOUP_DIRECTORY_INPUT_STREAM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM))
+#define WEBKIT_IS_SOUP_DIRECTORY_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM))
+#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, WebKitSoupDirectoryInputStreamClass))
+
+typedef struct _WebKitSoupDirectoryInputStream WebKitSoupDirectoryInputStream;
+typedef struct _WebKitSoupDirectoryInputStreamClass WebKitSoupDirectoryInputStreamClass;
+
+struct _WebKitSoupDirectoryInputStream {
+       GInputStream parent;
+
+       GFileEnumerator *enumerator;
+       char *uri;
+       SoupBuffer *buffer;
+       gboolean done;
+};
+
+struct _WebKitSoupDirectoryInputStreamClass {
+       GInputStreamClass parent_class;
+};
+
+GType          webkit_soup_directory_input_stream_get_type (void);
+
+GInputStream *webkit_soup_directory_input_stream_new (GFileEnumerator *enumerator,
+                                                     SoupURI         *uri);
+
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_H */
diff --git a/WebCore/platform/network/soup/cache/soup-http-input-stream.c b/WebCore/platform/network/soup/cache/soup-http-input-stream.c
new file mode 100644 (file)
index 0000000..dc95d6e
--- /dev/null
@@ -0,0 +1,921 @@
+/* soup-input-stream.c, based on gsocketinputstream.c
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libsoup/soup.h>
+
+#include "soup-http-input-stream.h"
+
+static void webkit_soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface);
+
+G_DEFINE_TYPE_WITH_CODE (WebKitSoupHTTPInputStream, webkit_soup_http_input_stream, G_TYPE_INPUT_STREAM,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
+                                               webkit_soup_http_input_stream_seekable_iface_init))
+
+typedef void (*WebKitSoupHTTPInputStreamCallback)(GInputStream *);
+
+typedef struct {
+       SoupSession *session;
+       GMainContext *async_context;
+       SoupMessage *msg;
+       gboolean got_headers, finished;
+       goffset offset;
+
+       GCancellable *cancellable;
+       GSource *cancel_watch;
+       WebKitSoupHTTPInputStreamCallback got_headers_cb;
+       WebKitSoupHTTPInputStreamCallback got_chunk_cb;
+       WebKitSoupHTTPInputStreamCallback finished_cb;
+       WebKitSoupHTTPInputStreamCallback cancelled_cb;
+
+       guchar *leftover_buffer;
+       gsize leftover_bufsize, leftover_offset;
+
+       guchar *caller_buffer;
+       gsize caller_bufsize, caller_nread;
+       GAsyncReadyCallback outstanding_callback;
+       GSimpleAsyncResult *result;
+} WebKitSoupHTTPInputStreamPrivate;
+#define WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStreamPrivate))
+
+
+static gssize   webkit_soup_http_input_stream_read (GInputStream         *stream,
+                                                   void                 *buffer,
+                                                   gsize count,
+                                                   GCancellable         *cancellable,
+                                                   GError              **error);
+static gboolean webkit_soup_http_input_stream_close (GInputStream         *stream,
+                                                    GCancellable         *cancellable,
+                                                    GError              **error);
+static void     webkit_soup_http_input_stream_read_async (GInputStream         *stream,
+                                                         void                 *buffer,
+                                                         gsize count,
+                                                         int io_priority,
+                                                         GCancellable         *cancellable,
+                                                         GAsyncReadyCallback callback,
+                                                         gpointer data);
+static gssize   webkit_soup_http_input_stream_read_finish (GInputStream         *stream,
+                                                          GAsyncResult         *result,
+                                                          GError              **error);
+static void     webkit_soup_http_input_stream_close_async (GInputStream         *stream,
+                                                          int io_priority,
+                                                          GCancellable         *cancellable,
+                                                          GAsyncReadyCallback callback,
+                                                          gpointer data);
+static gboolean webkit_soup_http_input_stream_close_finish (GInputStream         *stream,
+                                                           GAsyncResult         *result,
+                                                           GError              **error);
+
+static goffset  webkit_soup_http_input_stream_tell (GSeekable            *seekable);
+
+static gboolean webkit_soup_http_input_stream_can_seek (GSeekable            *seekable);
+static gboolean webkit_soup_http_input_stream_seek (GSeekable            *seekable,
+                                                   goffset offset,
+                                                   GSeekType type,
+                                                   GCancellable         *cancellable,
+                                                   GError              **error);
+
+static gboolean webkit_soup_http_input_stream_can_truncate (GSeekable            *seekable);
+static gboolean webkit_soup_http_input_stream_truncate (GSeekable            *seekable,
+                                                       goffset offset,
+                                                       GCancellable         *cancellable,
+                                                       GError              **error);
+
+static void webkit_soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream);
+static void webkit_soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer stream);
+static void webkit_soup_http_input_stream_finished (SoupMessage *msg, gpointer stream);
+
+static void
+webkit_soup_http_input_stream_finalize (GObject *object)
+{
+       WebKitSoupHTTPInputStream *stream = WEBKIT_SOUP_HTTP_INPUT_STREAM (object);
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+       g_object_unref (priv->session);
+
+       g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_got_headers), stream);
+       g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_got_chunk), stream);
+       g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_finished), stream);
+       g_object_unref (priv->msg);
+       g_free (priv->leftover_buffer);
+
+       if (G_OBJECT_CLASS (webkit_soup_http_input_stream_parent_class)->finalize)
+               (*G_OBJECT_CLASS (webkit_soup_http_input_stream_parent_class)->finalize)(object);
+}
+
+static void
+webkit_soup_http_input_stream_class_init (WebKitSoupHTTPInputStreamClass *klass)
+{
+       GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+       GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
+
+       g_type_class_add_private (klass, sizeof (WebKitSoupHTTPInputStreamPrivate));
+
+       gobject_class->finalize = webkit_soup_http_input_stream_finalize;
+
+       stream_class->read_fn = webkit_soup_http_input_stream_read;
+       stream_class->close_fn = webkit_soup_http_input_stream_close;
+       stream_class->read_async = webkit_soup_http_input_stream_read_async;
+       stream_class->read_finish = webkit_soup_http_input_stream_read_finish;
+       stream_class->close_async = webkit_soup_http_input_stream_close_async;
+       stream_class->close_finish = webkit_soup_http_input_stream_close_finish;
+}
+
+static void
+webkit_soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface)
+{
+       seekable_iface->tell = webkit_soup_http_input_stream_tell;
+       seekable_iface->can_seek = webkit_soup_http_input_stream_can_seek;
+       seekable_iface->seek = webkit_soup_http_input_stream_seek;
+       seekable_iface->can_truncate = webkit_soup_http_input_stream_can_truncate;
+       seekable_iface->truncate_fn = webkit_soup_http_input_stream_truncate;
+}
+
+static void
+webkit_soup_http_input_stream_init (WebKitSoupHTTPInputStream *stream)
+{
+       ;
+}
+
+static void
+webkit_soup_http_input_stream_queue_message (WebKitSoupHTTPInputStream *stream)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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);
+}
+
+/**
+ * webkit_soup_http_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 webkit_soup_http_input_stream_send() or webkit_soup_http_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 %WEBKIT_SOUP_HTTP_INPUT_STREAM_HTTP_ERROR.
+ *
+ * Internally, #WebKitSoupHTTPInputStream 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.
+ **/
+WebKitSoupHTTPInputStream *
+webkit_soup_http_input_stream_new (SoupSession *session, SoupMessage *msg)
+{
+       WebKitSoupHTTPInputStream *stream;
+       WebKitSoupHTTPInputStreamPrivate *priv;
+
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+       stream = g_object_new (WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, NULL);
+       priv = WEBKIT_SOUP_HTTP_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 (webkit_soup_http_input_stream_got_headers), stream);
+       g_signal_connect (msg, "got_chunk",
+                         G_CALLBACK (webkit_soup_http_input_stream_got_chunk), stream);
+       g_signal_connect (msg, "finished",
+                         G_CALLBACK (webkit_soup_http_input_stream_finished), stream);
+
+       webkit_soup_http_input_stream_queue_message (stream);
+       return stream;
+}
+
+static void
+webkit_soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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
+webkit_soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk_buffer,
+                                        gpointer stream)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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 ("webkit_soup_http_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
+webkit_soup_http_input_stream_finished (SoupMessage *msg, gpointer stream)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+       priv->finished = TRUE;
+
+       if (priv->finished_cb)
+               priv->finished_cb (stream);
+}
+
+static gboolean
+webkit_soup_http_input_stream_cancelled (GIOChannel *chan, GIOCondition condition,
+                                        gpointer stream)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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
+webkit_soup_http_input_stream_prepare_for_io (GInputStream *stream,
+                                             GCancellable *cancellable,
+                                             guchar       *buffer,
+                                             gsize count)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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,
+                                                       webkit_soup_http_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
+webkit_soup_http_input_stream_done_io (GInputStream *stream)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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 (WebKitSoupHTTPInputStreamPrivate *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 webkit_soup_http_input_stream_send(), assuming that the
+ * GInputStream pending flag has already been set. It is also used by
+ * webkit_soup_http_input_stream_send_async() in some circumstances.
+ */
+static gboolean
+webkit_soup_http_input_stream_send_internal (GInputStream  *stream,
+                                            GCancellable  *cancellable,
+                                            GError       **error)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+       webkit_soup_http_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);
+       webkit_soup_http_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;
+}
+
+static void
+send_sync_finished (GInputStream *stream)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+       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;
+
+       /* Wake up the main context iteration */
+       g_source_attach (g_idle_source_new (), NULL);
+}
+
+/**
+ * webkit_soup_http_input_stream_send:
+ * @httpstream: a #WebKitSoupHTTPInputStream
+ * @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 webkit_soup_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 has a successful (2xx) status, %FALSE if
+ * not.
+ **/
+gboolean
+webkit_soup_http_input_stream_send (WebKitSoupHTTPInputStream  *httpstream,
+                                   GCancellable         *cancellable,
+                                   GError              **error)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream);
+       GInputStream *istream = (GInputStream *)httpstream;
+       gboolean result;
+
+       g_return_val_if_fail (WEBKIT_IS_SOUP_HTTP_INPUT_STREAM (httpstream), FALSE);
+
+       if (!g_input_stream_set_pending (istream, error))
+               return FALSE;
+
+       priv->got_headers_cb = send_sync_finished;
+       priv->finished_cb = send_sync_finished;
+
+       result = webkit_soup_http_input_stream_send_internal (istream, cancellable, error);
+       g_input_stream_clear_pending (istream);
+
+       return result;
+}
+
+static gssize
+webkit_soup_http_input_stream_read (GInputStream *stream,
+                                   void         *buffer,
+                                   gsize count,
+                                   GCancellable *cancellable,
+                                   GError      **error)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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 */
+       webkit_soup_http_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);
+       webkit_soup_http_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
+webkit_soup_http_input_stream_close (GInputStream *stream,
+                                    GCancellable *cancellable,
+                                    GError      **error)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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);
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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 = webkit_soup_http_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
+webkit_soup_http_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,
+                                        webkit_soup_http_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)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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;
+       webkit_soup_http_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);
+}
+
+static void
+webkit_soup_http_input_stream_send_async_internal (GInputStream        *stream,
+                                                  int io_priority,
+                                                  GCancellable        *cancellable,
+                                                  GAsyncReadyCallback callback,
+                                                  gpointer user_data)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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)) {
+               webkit_soup_http_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;
+
+       webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, NULL, 0);
+       priv->result = g_simple_async_result_new (G_OBJECT (stream),
+                                                 wrapper_callback, user_data,
+                                                 webkit_soup_http_input_stream_send_async);
+}
+
+/**
+ * webkit_soup_http_input_stream_send_async:
+ * @httpstream: a #WebKitSoupHTTPInputStream
+ * @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 webkit_soup_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
+webkit_soup_http_input_stream_send_async (WebKitSoupHTTPInputStream *httpstream,
+                                         int io_priority,
+                                         GCancellable        *cancellable,
+                                         GAsyncReadyCallback callback,
+                                         gpointer user_data)
+{
+       GInputStream *istream = (GInputStream *)httpstream;
+       GError *error = NULL;
+
+       g_return_if_fail (WEBKIT_IS_SOUP_HTTP_INPUT_STREAM (httpstream));
+
+       if (!g_input_stream_set_pending (istream, &error)) {
+               g_simple_async_report_gerror_in_idle (G_OBJECT (httpstream),
+                                                     callback,
+                                                     user_data,
+                                                     error);
+               g_error_free (error);
+               return;
+       }
+       webkit_soup_http_input_stream_send_async_internal (istream, io_priority, cancellable,
+                                                   callback, user_data);
+}
+
+/**
+ * webkit_soup_http_input_stream_send_finish:
+ * @httpstream: a #WebKitSoupHTTPInputStream
+ * @result: a #GAsyncResult.
+ * @error: a #GError location to store the error occuring, or %NULL to
+ * ignore.
+ *
+ * Finishes a webkit_soup_http_input_stream_send_async() operation.
+ *
+ * Return value: %TRUE if the message was sent successfully and
+ * received a successful status code, %FALSE if not.
+ **/
+gboolean
+webkit_soup_http_input_stream_send_finish (WebKitSoupHTTPInputStream  *httpstream,
+                                          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) == webkit_soup_http_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)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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;
+       webkit_soup_http_input_stream_done_io (stream);
+
+       g_simple_async_result_complete (result);
+       g_object_unref (result);
+}
+
+static void
+webkit_soup_http_input_stream_read_async (GInputStream        *stream,
+                                         void                *buffer,
+                                         gsize count,
+                                         int io_priority,
+                                         GCancellable        *cancellable,
+                                         GAsyncReadyCallback callback,
+                                         gpointer user_data)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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 (webkit_soup_http_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,
+                                           webkit_soup_http_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;
+       webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, buffer, count);
+}
+
+static gssize
+webkit_soup_http_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) == webkit_soup_http_input_stream_read_async, -1);
+
+       return g_simple_async_result_get_op_res_gssize (simple);
+}
+
+static void
+webkit_soup_http_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,
+                                           webkit_soup_http_input_stream_close_async);
+       success = webkit_soup_http_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
+webkit_soup_http_input_stream_close_finish (GInputStream  *stream,
+                                           GAsyncResult  *result,
+                                           GError       **error)
+{
+       /* Failures handled in generic close_finish code */
+       return TRUE;
+}
+
+static goffset
+webkit_soup_http_input_stream_tell (GSeekable *seekable)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable);
+
+       return priv->offset;
+}
+
+static gboolean
+webkit_soup_http_input_stream_can_seek (GSeekable *seekable)
+{
+       return TRUE;
+}
+
+extern void soup_message_io_cleanup (SoupMessage *msg);
+
+static gboolean
+webkit_soup_http_input_stream_seek (GSeekable     *seekable,
+                                   goffset offset,
+                                   GSeekType type,
+                                   GCancellable  *cancellable,
+                                   GError       **error)
+{
+       GInputStream *stream = G_INPUT_STREAM (seekable);
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_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);
+
+       webkit_soup_http_input_stream_queue_message (WEBKIT_SOUP_HTTP_INPUT_STREAM (stream));
+
+       g_input_stream_clear_pending (stream);
+       return TRUE;
+}
+
+static gboolean
+webkit_soup_http_input_stream_can_truncate (GSeekable *seekable)
+{
+       return FALSE;
+}
+
+static gboolean
+webkit_soup_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 *
+webkit_soup_http_input_stream_get_message (WebKitSoupHTTPInputStream *httpstream)
+{
+       WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream);
+       return priv->msg ? g_object_ref (priv->msg) : NULL;
+}
diff --git a/WebCore/platform/network/soup/cache/soup-http-input-stream.h b/WebCore/platform/network/soup/cache/soup-http-input-stream.h
new file mode 100644 (file)
index 0000000..6b98559
--- /dev/null
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, 2007, 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __WEBKIT_SOUP_HTTP_INPUT_STREAM_H__
+#define __WEBKIT_SOUP_HTTP_INPUT_STREAM_H__
+
+#include <gio/gio.h>
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM         (webkit_soup_http_input_stream_get_type ())
+#define WEBKIT_SOUP_HTTP_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStream))
+#define WEBKIT_SOUP_HTTP_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStreamClass))
+#define WEBKIT_IS_SOUP_HTTP_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM))
+#define WEBKIT_IS_SOUP_HTTP_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM))
+#define WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStreamClass))
+
+typedef struct WebKitSoupHTTPInputStream WebKitSoupHTTPInputStream;
+typedef struct WebKitSoupHTTPInputStreamClass WebKitSoupHTTPInputStreamClass;
+
+struct WebKitSoupHTTPInputStream {
+       GInputStream parent;
+};
+
+struct WebKitSoupHTTPInputStreamClass {
+       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 webkit_soup_http_input_stream_get_type (void) G_GNUC_CONST;
+
+WebKitSoupHTTPInputStream *webkit_soup_http_input_stream_new (SoupSession         *session,
+                                                             SoupMessage         *msg);
+
+gboolean             webkit_soup_http_input_stream_send (WebKitSoupHTTPInputStream *httpstream,
+                                                        GCancellable        *cancellable,
+                                                        GError             **error);
+
+void                 webkit_soup_http_input_stream_send_async (WebKitSoupHTTPInputStream *httpstream,
+                                                              int io_priority,
+                                                              GCancellable        *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
+gboolean             webkit_soup_http_input_stream_send_finish (WebKitSoupHTTPInputStream *httpstream,
+                                                               GAsyncResult        *result,
+                                                               GError             **error);
+
+SoupMessage         *webkit_soup_http_input_stream_get_message (WebKitSoupHTTPInputStream *httpstream);
+
+G_END_DECLS
+
+#endif /* __WEBKIT_SOUP_HTTP_INPUT_STREAM_H__ */
diff --git a/WebCore/platform/network/soup/cache/soup-request-data.c b/WebCore/platform/network/soup/cache/soup-request-data.c
new file mode 100644 (file)
index 0000000..9849269
--- /dev/null
@@ -0,0 +1,170 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-request-data.c: data: URI request object
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "soup-request-data.h"
+
+#include "soup-requester.h"
+#include <libsoup/soup.h>
+#include <glib/gi18n.h>
+
+G_DEFINE_TYPE (WebKitSoupRequestData, webkit_soup_request_data, WEBKIT_TYPE_SOUP_REQUEST)
+
+struct _WebKitSoupRequestDataPrivate {
+       gsize content_length;
+       char *content_type;
+};
+
+static void
+webkit_soup_request_data_init (WebKitSoupRequestData *data)
+{
+       data->priv = G_TYPE_INSTANCE_GET_PRIVATE (data, WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestDataPrivate);
+}
+
+static void
+webkit_soup_request_data_finalize (GObject *object)
+{
+       WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (object);
+
+       g_free (data->priv->content_type);
+
+       G_OBJECT_CLASS (webkit_soup_request_data_parent_class)->finalize (object);
+}
+
+static gboolean
+webkit_soup_request_data_check_uri (WebKitSoupRequest  *request,
+                                   SoupURI      *uri,
+                                   GError      **error)
+{
+       return uri->host == NULL;
+}
+
+static GInputStream *
+webkit_soup_request_data_send (WebKitSoupRequest   *request,
+                              GCancellable  *cancellable,
+                              GError       **error)
+{
+       WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (request);
+       SoupURI *uri = webkit_soup_request_get_uri (request);
+       GInputStream *memstream;
+       const char *comma, *semi, *start, *end;
+       gboolean base64 = FALSE;
+
+       gchar *uristr = soup_uri_to_string (uri, FALSE);
+       comma = strchr (uristr, ',');
+       if (comma && comma != uristr) {
+               /* Deal with MIME type / params */
+               semi = memchr (uristr, ';', comma - uristr);
+               end = semi ? semi : comma;
+
+               if (semi && !g_ascii_strncasecmp (semi, ";base64", MAX (comma - semi, strlen (";base64"))))
+                       base64 = TRUE;
+
+               if (end != uristr)
+                       if (base64)
+                               data->priv->content_type = g_strndup (uristr, end - uristr);
+                       else
+                               data->priv->content_type =
+                                       webkit_soup_request_uri_decoded_copy (uristr, end - uristr);
+       }
+
+       memstream = g_memory_input_stream_new ();
+
+       start = comma ? comma + 1 : uristr;
+
+       if (*start) {
+               guchar *buf;
+
+               if (base64) {
+                       int inlen, state = 0;
+                       guint save = 0;
+
+                       inlen = strlen (start);
+                       buf = g_malloc (inlen * 3 / 4);
+                       data->priv->content_length =
+                               g_base64_decode_step (start, inlen, buf,
+                                                     &state, &save);
+                       if (state != 0) {
+                               g_free (buf);
+                               goto fail;
+                       }
+               } else {
+                       /* Cannot use g_uri_unescape_string nor
+                          soup_uri_decode because we don't want to
+                          fail for things like "%3E%%3C" -> ">%<" */
+                       buf = (guchar *)webkit_soup_request_uri_decoded_copy (start, strlen (start));
+                       if (!buf)
+                               goto fail;
+                       data->priv->content_length = strlen ((char *)buf);
+               }
+
+               g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (memstream),
+                                               buf, data->priv->content_length,
+                                               g_free);
+       }
+       g_free (uristr);
+
+       return memstream;
+
+ fail:
+       g_free (uristr);
+       g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI,
+                    _ ("Unable to decode URI: %s"), start);
+       g_object_unref (memstream);
+       return NULL;
+}
+
+static goffset
+webkit_soup_request_data_get_content_length (WebKitSoupRequest *request)
+{
+       WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (request);
+
+       return data->priv->content_length;
+}
+
+static const char *
+webkit_soup_request_data_get_content_type (WebKitSoupRequest *request)
+{
+       WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (request);
+
+       return data->priv->content_type;
+}
+
+static void
+webkit_soup_request_data_class_init (WebKitSoupRequestDataClass *request_data_class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (request_data_class);
+       WebKitSoupRequestClass *request_class =
+               WEBKIT_SOUP_REQUEST_CLASS (request_data_class);
+
+       g_type_class_add_private (request_data_class, sizeof (WebKitSoupRequestDataPrivate));
+
+       object_class->finalize = webkit_soup_request_data_finalize;
+
+       request_class->check_uri = webkit_soup_request_data_check_uri;
+       request_class->send = webkit_soup_request_data_send;
+       request_class->get_content_length = webkit_soup_request_data_get_content_length;
+       request_class->get_content_type = webkit_soup_request_data_get_content_type;
+}
diff --git a/WebCore/platform/network/soup/cache/soup-request-data.h b/WebCore/platform/network/soup/cache/soup-request-data.h
new file mode 100644 (file)
index 0000000..c9631a4
--- /dev/null
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_REQUEST_DATA_H
+#define WEBKIT_SOUP_REQUEST_DATA_H 1
+
+#include "soup-request.h"
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_REQUEST_DATA            (webkit_soup_request_data_get_type ())
+#define WEBKIT_SOUP_REQUEST_DATA(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestData))
+#define WEBKIT_SOUP_REQUEST_DATA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestDataClass))
+#define WEBKIT_IS_SOUP_REQUEST_DATA(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), WEBKIT_TYPE_SOUP_REQUEST_DATA))
+#define WEBKIT_IS_SOUP_REQUEST_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST_DATA))
+#define WEBKIT_SOUP_REQUEST_DATA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestDataClass))
+
+typedef struct _WebKitSoupRequestDataPrivate WebKitSoupRequestDataPrivate;
+
+typedef struct {
+       WebKitSoupRequest parent;
+
+       WebKitSoupRequestDataPrivate *priv;
+} WebKitSoupRequestData;
+
+typedef struct {
+       WebKitSoupRequestClass parent;
+} WebKitSoupRequestDataClass;
+
+GType webkit_soup_request_data_get_type (void);
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_REQUEST_DATA_H */
diff --git a/WebCore/platform/network/soup/cache/soup-request-file.c b/WebCore/platform/network/soup/cache/soup-request-file.c
new file mode 100644 (file)
index 0000000..19dedb6
--- /dev/null
@@ -0,0 +1,332 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-request-file.c: file: URI request object
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-request-file.h"
+#include "soup-directory-input-stream.h"
+#include "soup-requester.h"
+#include <glib/gi18n.h>
+
+G_DEFINE_TYPE (WebKitSoupRequestFile, webkit_soup_request_file, WEBKIT_TYPE_SOUP_REQUEST)
+
+struct _WebKitSoupRequestFilePrivate {
+       GFile *gfile;
+
+       char *mime_type;
+       goffset size;
+};
+
+GFile *
+webkit_soup_request_file_get_file (WebKitSoupRequestFile *file)
+{
+       return g_object_ref (file->priv->gfile);
+}
+
+static void
+webkit_soup_request_file_init (WebKitSoupRequestFile *file)
+{
+       file->priv = G_TYPE_INSTANCE_GET_PRIVATE (file, WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFilePrivate);
+
+       file->priv->size = -1;
+}
+
+static void
+webkit_soup_request_file_finalize (GObject *object)
+{
+       WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (object);
+
+       if (file->priv->gfile)
+               g_object_unref (file->priv->gfile);
+       g_free (file->priv->mime_type);
+
+       G_OBJECT_CLASS (webkit_soup_request_file_parent_class)->finalize (object);
+}
+
+static gboolean
+webkit_soup_request_file_check_uri (WebKitSoupRequest  *request,
+                                   SoupURI      *uri,
+                                   GError      **error)
+{
+       /* "file:/foo" is not valid */
+       if (!uri->host)
+               return FALSE;
+
+       /* but it must be "file:///..." or "file://localhost/..." */
+       if (uri->scheme == SOUP_URI_SCHEME_FILE &&
+           *uri->host &&
+           g_ascii_strcasecmp (uri->host, "localhost") != 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+static void
+webkit_soup_request_file_ftp_main_loop_quit (GObject      *object,
+                                            GAsyncResult *result,
+                                            gpointer loop)
+{
+       g_main_loop_quit (loop);
+}
+
+/* This is a somewhat hacky way to get FTP to almost work. The proper way to
+ * get FTP to _really_ work involves hacking GIO to have APIs to handle
+ * canoncial URLs.
+ */
+static GFile *
+webkit_soup_request_file_ensure_file_ftp (SoupURI       *uri,
+                                         GCancellable  *cancellable,
+                                         GError       **error)
+{
+       SoupURI *host;
+       char *s;
+       GFile *file, *result;
+       GMount *mount;
+
+       host = soup_uri_copy_host (uri);
+       s = soup_uri_to_string (host, FALSE);
+       file = g_file_new_for_uri (s);
+       soup_uri_free (host);
+       g_free (s);
+
+       mount = g_file_find_enclosing_mount (file, cancellable, error);
+       if (mount == NULL && g_file_supports_thread_contexts (file)) {
+               GMainContext *context = g_main_context_new ();
+               GMainLoop *loop = g_main_loop_new (context, FALSE);
+
+               g_clear_error (error);
+               g_main_context_push_thread_default (context);
+               g_file_mount_enclosing_volume (file,
+                                              G_MOUNT_MOUNT_NONE,
+                                              NULL, /* FIXME! */
+                                              cancellable,
+                                              webkit_soup_request_file_ftp_main_loop_quit,
+                                              loop);
+               g_main_loop_run (loop);
+               g_main_context_pop_thread_default (context);
+               g_main_loop_unref (loop);
+               g_main_context_unref (context);
+               mount = g_file_find_enclosing_mount (file, cancellable, error);
+       }
+       if (mount == NULL)
+               return NULL;
+       g_object_unref (file);
+
+       file = g_mount_get_default_location (mount);
+       g_object_unref (mount);
+
+       s = g_strdup (uri->path);
+       if (strchr (s, ';'))
+               *strchr (s, ';') = 0;
+
+       result = g_file_resolve_relative_path (file, s);
+       g_free (s);
+       g_object_unref (file);
+
+       return result;
+}
+
+static gboolean
+webkit_soup_request_file_ensure_file (WebKitSoupRequestFile  *file,
+                                     GCancellable     *cancellable,
+                                     GError          **error)
+{
+       SoupURI *uri;
+
+       if (file->priv->gfile)
+               return TRUE;
+
+       uri = webkit_soup_request_get_uri (WEBKIT_SOUP_REQUEST (file));
+       if (uri->scheme == SOUP_URI_SCHEME_FILE) {
+               /* We cannot use soup_uri_decode as it incorrectly
+                * returns NULL for incorrectly encoded URIs (that
+                * could be valid filenames). This will be hopefully
+                * shipped in libsoup 2.32.1 but we want to land this
+                * first. TODO: replace uri_decoded_copy by
+                * soup_uri_decode when the required libsoup version
+                * is bumped out to 2.32.1
+                */
+               gchar *uri_path = soup_uri_get_path (uri);
+               gchar *decoded_uri = webkit_soup_request_uri_decoded_copy (uri_path, strlen (uri_path));
+
+               if (decoded_uri) {
+                       /* Do not use new_for_uri() as the decoded URI
+                        *  could not be a valid URI
+                        */
+                       file->priv->gfile = g_file_new_for_path (decoded_uri);
+                       g_free (decoded_uri);
+               }
+
+               return TRUE;
+       } else if (uri->scheme == SOUP_URI_SCHEME_FTP) {
+               file->priv->gfile = webkit_soup_request_file_ensure_file_ftp (uri,
+                                                                             cancellable,
+                                                                             error);
+               return file->priv->gfile != NULL;
+       }
+
+       g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_UNSUPPORTED_URI_SCHEME,
+                    _ ("Unsupported URI scheme '%s'"), uri->scheme);
+       return FALSE;
+}
+
+static GInputStream *
+webkit_soup_request_file_send (WebKitSoupRequest          *request,
+                              GCancellable         *cancellable,
+                              GError              **error)
+{
+       WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (request);
+       GInputStream *stream;
+       GError *my_error = NULL;
+
+       if (!webkit_soup_request_file_ensure_file (file, cancellable, error))
+               return NULL;
+
+       stream = G_INPUT_STREAM (g_file_read (file->priv->gfile,
+                                             cancellable, &my_error));
+       if (stream == NULL) {
+               if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)) {
+                       GFileEnumerator *enumerator;
+                       g_clear_error (&my_error);
+                       enumerator = g_file_enumerate_children (file->priv->gfile,
+                                                               "*",
+                                                               G_FILE_QUERY_INFO_NONE,
+                                                               cancellable,
+                                                               error);
+                       if (enumerator) {
+                               stream = webkit_soup_directory_input_stream_new (enumerator,
+                                                                                webkit_soup_request_get_uri (request));
+                               g_object_unref (enumerator);
+                               file->priv->mime_type = g_strdup ("text/html");
+                       }
+               } else {
+                       g_propagate_error (error, my_error);
+               }
+       } else {
+               GFileInfo *info = g_file_query_info (file->priv->gfile,
+                                                    G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
+                                                    G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                                                    0, cancellable, NULL);
+               if (info) {
+                       const char *content_type;
+                       file->priv->size = g_file_info_get_size (info);
+                       content_type = g_file_info_get_content_type (info);
+
+                       if (content_type)
+                               file->priv->mime_type = g_content_type_get_mime_type (content_type);
+                       g_object_unref (info);
+               }
+       }
+
+       return stream;
+}
+
+static void
+webkit_soup_request_file_send_async_thread (GSimpleAsyncResult *res,
+                                           GObject            *object,
+                                           GCancellable       *cancellable)
+{
+       GInputStream *stream;
+       WebKitSoupRequest *request;
+       GError *error = NULL;
+
+       request = WEBKIT_SOUP_REQUEST (object);
+
+       stream = webkit_soup_request_file_send (request, cancellable, &error);
+
+       if (stream == NULL) {
+               g_simple_async_result_set_from_error (res, error);
+               g_error_free (error);
+       } else {
+               g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
+       }
+}
+
+static void
+webkit_soup_request_file_send_async (WebKitSoupRequest          *request,
+                                    GCancellable         *cancellable,
+                                    GAsyncReadyCallback callback,
+                                    gpointer user_data)
+{
+       GSimpleAsyncResult *res;
+
+       res = g_simple_async_result_new (G_OBJECT (request), callback, user_data, webkit_soup_request_file_send_async);
+
+       g_simple_async_result_run_in_thread (res, webkit_soup_request_file_send_async_thread, G_PRIORITY_DEFAULT, cancellable);
+       g_object_unref (res);
+}
+
+static GInputStream *
+webkit_soup_request_file_send_finish (WebKitSoupRequest          *request,
+                                     GAsyncResult         *result,
+                                     GError              **error)
+{
+       GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+       g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == webkit_soup_request_file_send_async);
+
+       if (g_simple_async_result_propagate_error (simple, error))
+               return NULL;
+
+       return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+static goffset
+webkit_soup_request_file_get_content_length (WebKitSoupRequest *request)
+{
+       WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (request);
+
+       return file->priv->size;
+}
+
+static const char *
+webkit_soup_request_file_get_content_type (WebKitSoupRequest *request)
+{
+       WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (request);
+
+       if (!file->priv->mime_type)
+               return "application/octet-stream";
+
+       return file->priv->mime_type;
+}
+
+static void
+webkit_soup_request_file_class_init (WebKitSoupRequestFileClass *request_file_class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (request_file_class);
+       WebKitSoupRequestClass *request_class =
+               WEBKIT_SOUP_REQUEST_CLASS (request_file_class);
+
+       g_type_class_add_private (request_file_class, sizeof (WebKitSoupRequestFilePrivate));
+
+       object_class->finalize = webkit_soup_request_file_finalize;
+
+       request_class->check_uri = webkit_soup_request_file_check_uri;
+       request_class->send = webkit_soup_request_file_send;
+       request_class->send_async = webkit_soup_request_file_send_async;
+       request_class->send_finish = webkit_soup_request_file_send_finish;
+       request_class->get_content_length = webkit_soup_request_file_get_content_length;
+       request_class->get_content_type = webkit_soup_request_file_get_content_type;
+}
diff --git a/WebCore/platform/network/soup/cache/soup-request-file.h b/WebCore/platform/network/soup/cache/soup-request-file.h
new file mode 100644 (file)
index 0000000..459e82a
--- /dev/null
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_REQUEST_FILE_H
+#define WEBKIT_SOUP_REQUEST_FILE_H 1
+
+#include "soup-request.h"
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_REQUEST_FILE            (webkit_soup_request_file_get_type ())
+#define WEBKIT_SOUP_REQUEST_FILE(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFile))
+#define WEBKIT_SOUP_REQUEST_FILE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFileClass))
+#define WEBKIT_IS_SOUP_REQUEST_FILE(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), WEBKIT_TYPE_SOUP_REQUEST_FILE))
+#define WEBKIT_IS_SOUP_REQUEST_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST_FILE))
+#define WEBKIT_SOUP_REQUEST_FILE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFileClass))
+
+typedef struct _WebKitSoupRequestFilePrivate WebKitSoupRequestFilePrivate;
+
+typedef struct {
+       WebKitSoupRequest parent;
+
+       WebKitSoupRequestFilePrivate *priv;
+} WebKitSoupRequestFile;
+
+typedef struct {
+       WebKitSoupRequestClass parent;
+} WebKitSoupRequestFileClass;
+
+GType webkit_soup_request_file_get_type (void);
+
+GFile  *webkit_soup_request_file_get_file (WebKitSoupRequestFile *file);
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_REQUEST_FILE_H */
diff --git a/WebCore/platform/network/soup/cache/soup-request-http.c b/WebCore/platform/network/soup/cache/soup-request-http.c
new file mode 100644 (file)
index 0000000..f157cfc
--- /dev/null
@@ -0,0 +1,340 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-request-http.c: http: URI request object
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#include "soup-cache.h"
+#include "soup-cache-private.h"
+#include "soup-http-input-stream.h"
+#include "soup-request-http.h"
+
+G_DEFINE_TYPE (WebKitSoupRequestHTTP, webkit_soup_request_http, WEBKIT_TYPE_SOUP_REQUEST)
+
+struct _WebKitSoupRequestHTTPPrivate {
+       SoupMessage *msg;
+};
+
+/**
+ * webkit_soup_request_http_get_message:
+ * @http: a #WebKitSoupRequestHTTP object
+ *
+ * Gets a new reference to the #SoupMessage associated to this SoupRequest
+ *
+ * Returns: a new reference to the #SoupMessage
+ **/
+SoupMessage *
+webkit_soup_request_http_get_message (WebKitSoupRequestHTTP *http)
+{
+       g_return_val_if_fail (WEBKIT_IS_SOUP_REQUEST_HTTP (http), NULL);
+
+       return g_object_ref (http->priv->msg);
+}
+
+static void
+webkit_soup_request_http_init (WebKitSoupRequestHTTP *http)
+{
+       http->priv = G_TYPE_INSTANCE_GET_PRIVATE (http, WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPPrivate);
+}
+
+static gboolean
+webkit_soup_request_http_check_uri (WebKitSoupRequest  *request,
+                                   SoupURI      *uri,
+                                   GError      **error)
+{
+       WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
+
+       if (!SOUP_URI_VALID_FOR_HTTP (uri))
+               return FALSE;
+
+       http->priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+       return TRUE;
+}
+
+static void
+webkit_soup_request_http_finalize (GObject *object)
+{
+       WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (object);
+
+       if (http->priv->msg)
+               g_object_unref (http->priv->msg);
+
+       G_OBJECT_CLASS (webkit_soup_request_http_parent_class)->finalize (object);
+}
+
+static GInputStream *
+webkit_soup_request_http_send (WebKitSoupRequest          *request,
+                              GCancellable         *cancellable,
+                              GError              **error)
+{
+       WebKitSoupHTTPInputStream *httpstream;
+       WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
+
+       httpstream = webkit_soup_http_input_stream_new (webkit_soup_request_get_session (request), http->priv->msg);
+       if (!webkit_soup_http_input_stream_send (httpstream, cancellable, error)) {
+               g_object_unref (httpstream);
+               return NULL;
+       }
+       return (GInputStream *)httpstream;
+}
+
+
+static void
+sent_async (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+       WebKitSoupHTTPInputStream *httpstream = WEBKIT_SOUP_HTTP_INPUT_STREAM (source);
+       GSimpleAsyncResult *simple = user_data;
+       GError *error = NULL;
+
+       if (webkit_soup_http_input_stream_send_finish (httpstream, result, &error)) {
+               g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
+       } else {
+               g_simple_async_result_set_from_error (simple, error);
+               g_error_free (error);
+               g_object_unref (httpstream);
+       }
+       g_simple_async_result_complete (simple);
+       g_object_unref (simple);
+}
+
+
+typedef struct {
+       WebKitSoupRequestHTTP *req;
+       SoupMessage *original;
+       GCancellable *cancellable;
+       GAsyncReadyCallback callback;
+       gpointer user_data;
+} ConditionalHelper;
+
+
+static void
+conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+       ConditionalHelper *helper = (ConditionalHelper *)user_data;
+       GSimpleAsyncResult *simple;
+       WebKitSoupHTTPInputStream *httpstream;
+
+       simple = g_simple_async_result_new (G_OBJECT (helper->req),
+                                           helper->callback, helper->user_data,
+                                           conditional_get_ready_cb);
+
+       if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
+               WebKitSoupCache *cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
+
+               httpstream = (WebKitSoupHTTPInputStream *)webkit_soup_cache_send_response (cache, msg);
+               if (httpstream) {
+                       const gchar *content_type;
+
+                       g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
+
+                       soup_message_got_headers (helper->original);
+                       content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
+                       soup_message_content_sniffed (helper->original, content_type, NULL);
+
+                       g_simple_async_result_complete (simple);
+
+                       soup_message_finished (helper->original);
+
+                       g_object_unref (simple);
+               } else {
+                       /* Ask again for the resource, somehow the cache cannot locate it */
+                       httpstream = webkit_soup_http_input_stream_new (session, helper->original);
+                       webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
+                                                                 helper->cancellable, sent_async, simple);
+               }
+       } else {
+               /* It is in the cache but it was modified remotely */
+               httpstream = webkit_soup_http_input_stream_new (session, helper->original);
+               webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
+                                                         helper->cancellable, sent_async, simple);
+       }
+
+       g_object_unref (helper->req);
+       g_object_unref (helper->original);
+       g_slice_free (ConditionalHelper, helper);
+}
+
+typedef struct {
+       WebKitSoupRequestHTTP *http;
+       GAsyncReadyCallback callback;
+       gpointer user_data;
+} SendAsyncHelper;
+
+static void webkit_soup_request_http_send_async (WebKitSoupRequest          *request,
+                                                GCancellable         *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+
+static gboolean
+send_async_cb (gpointer data)
+{
+       GSimpleAsyncResult *simple;
+       WebKitSoupHTTPInputStream *httpstream;
+       SoupSession *session;
+       WebKitSoupCache *cache;
+       SendAsyncHelper *helper = (SendAsyncHelper *)data;
+
+       session = webkit_soup_request_get_session (WEBKIT_SOUP_REQUEST (helper->http));
+       cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
+
+       httpstream = (WebKitSoupHTTPInputStream *)webkit_soup_cache_send_response (cache, SOUP_MESSAGE (helper->http->priv->msg));
+
+       if (httpstream) {
+               const gchar *content_type;
+
+               simple = g_simple_async_result_new (G_OBJECT (helper->http),
+                                                   helper->callback, helper->user_data,
+                                                   webkit_soup_request_http_send_async);
+               g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
+
+               /* Update message status */
+               soup_message_set_status (helper->http->priv->msg, SOUP_STATUS_OK);
+
+               /* Issue signals  */
+               soup_message_got_headers (helper->http->priv->msg);
+               content_type = soup_message_headers_get_content_type (helper->http->priv->msg->response_headers, NULL);
+               soup_message_content_sniffed (helper->http->priv->msg, content_type, NULL);
+
+               g_simple_async_result_complete (simple);
+
+               soup_message_finished (helper->http->priv->msg);
+
+               g_object_unref (simple);
+       }
+
+       g_slice_free (SendAsyncHelper, helper);
+
+       return FALSE;
+}
+
+static void
+webkit_soup_request_http_send_async (WebKitSoupRequest          *request,
+                                    GCancellable         *cancellable,
+                                    GAsyncReadyCallback callback,
+                                    gpointer user_data)
+{
+       WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
+       WebKitSoupHTTPInputStream *httpstream;
+       GSimpleAsyncResult *simple;
+       SoupSession *session;
+       WebKitSoupCache *cache;
+
+       session = webkit_soup_request_get_session (request);
+       cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
+
+       if (cache) {
+               WebKitSoupCacheResponse response;
+
+               response = webkit_soup_cache_has_response (cache, http->priv->msg);
+               if (response == WEBKIT_SOUP_CACHE_RESPONSE_FRESH) {
+                       /* Do return the stream asynchronously as in
+                          the other cases. It's not enough to use
+                          g_simple_async_result_complete_in_idle as
+                          the signals must be also emitted
+                          asynchronously */
+                       SendAsyncHelper *helper = g_slice_new (SendAsyncHelper);
+                       helper->http = http;
+                       helper->callback = callback;
+                       helper->user_data = user_data;
+                       g_timeout_add (0, send_async_cb, helper);
+                       return;
+               } else if (response == WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
+                       SoupMessage *conditional_msg;
+                       ConditionalHelper *helper;
+
+                       conditional_msg = webkit_soup_cache_generate_conditional_request (cache, http->priv->msg);
+
+                       helper = g_slice_new0 (ConditionalHelper);
+                       helper->req = g_object_ref (http);
+                       helper->original = g_object_ref (http->priv->msg);
+                       helper->cancellable = cancellable;
+                       helper->callback = callback;
+                       helper->user_data = user_data;
+                       soup_session_queue_message (session, conditional_msg,
+                                                   conditional_get_ready_cb,
+                                                   helper);
+                       return;
+               }
+       }
+
+       simple = g_simple_async_result_new (G_OBJECT (http),
+                                           callback, user_data,
+                                           webkit_soup_request_http_send_async);
+       httpstream = webkit_soup_http_input_stream_new (webkit_soup_request_get_session (request),
+                                                       http->priv->msg);
+       webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
+                                                 cancellable, sent_async, simple);
+}
+
+static GInputStream *
+webkit_soup_request_http_send_finish (WebKitSoupRequest          *request,
+                                     GAsyncResult         *result,
+                                     GError              **error)
+{
+       GSimpleAsyncResult *simple;
+
+       g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), webkit_soup_request_http_send_async) || g_simple_async_result_is_valid (result, G_OBJECT (request), conditional_get_ready_cb), NULL);
+
+       simple = G_SIMPLE_ASYNC_RESULT (result);
+       if (g_simple_async_result_propagate_error (simple, error))
+               return NULL;
+       return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+static goffset
+webkit_soup_request_http_get_content_length (WebKitSoupRequest *request)
+{
+       WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
+
+       return soup_message_headers_get_content_length (http->priv->msg->response_headers);
+}
+
+static const char *
+webkit_soup_request_http_get_content_type (WebKitSoupRequest *request)
+{
+       WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
+
+       return soup_message_headers_get_content_type (http->priv->msg->response_headers, NULL);
+}
+
+static void
+webkit_soup_request_http_class_init (WebKitSoupRequestHTTPClass *request_http_class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (request_http_class);
+       WebKitSoupRequestClass *request_class =
+               WEBKIT_SOUP_REQUEST_CLASS (request_http_class);
+
+       g_type_class_add_private (request_http_class, sizeof (WebKitSoupRequestHTTPPrivate));
+
+       object_class->finalize = webkit_soup_request_http_finalize;
+
+       request_class->check_uri = webkit_soup_request_http_check_uri;
+       request_class->send = webkit_soup_request_http_send;
+       request_class->send_async = webkit_soup_request_http_send_async;
+       request_class->send_finish = webkit_soup_request_http_send_finish;
+       request_class->get_content_length = webkit_soup_request_http_get_content_length;
+       request_class->get_content_type = webkit_soup_request_http_get_content_type;
+}
diff --git a/WebCore/platform/network/soup/cache/soup-request-http.h b/WebCore/platform/network/soup/cache/soup-request-http.h
new file mode 100644 (file)
index 0000000..a06a821
--- /dev/null
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_REQUEST_HTTP_H
+#define WEBKIT_SOUP_REQUEST_HTTP_H 1
+
+#include "soup-request.h"
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_REQUEST_HTTP            (webkit_soup_request_http_get_type ())
+#define WEBKIT_SOUP_REQUEST_HTTP(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTP))
+#define WEBKIT_SOUP_REQUEST_HTTP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPClass))
+#define WEBKIT_IS_SOUP_REQUEST_HTTP(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), WEBKIT_TYPE_SOUP_REQUEST_HTTP))
+#define WEBKIT_IS_SOUP_REQUEST_HTTP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST_HTTP))
+#define WEBKIT_SOUP_REQUEST_HTTP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPClass))
+
+typedef struct _WebKitSoupRequestHTTPPrivate WebKitSoupRequestHTTPPrivate;
+
+typedef struct {
+       WebKitSoupRequest parent;
+
+       WebKitSoupRequestHTTPPrivate *priv;
+} WebKitSoupRequestHTTP;
+
+typedef struct {
+       WebKitSoupRequestClass parent;
+} WebKitSoupRequestHTTPClass;
+
+GType webkit_soup_request_http_get_type (void);
+
+SoupMessage  *webkit_soup_request_http_get_message (WebKitSoupRequestHTTP *http);
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_REQUEST_HTTP_H */
diff --git a/WebCore/platform/network/soup/cache/soup-request.c b/WebCore/platform/network/soup/cache/soup-request.c
new file mode 100644 (file)
index 0000000..46b9f5a
--- /dev/null
@@ -0,0 +1,312 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-request.c: Protocol-independent streaming request interface
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010, Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#include "soup-request.h"
+#include "soup-requester.h"
+
+/**
+ * SECTION:soup-request
+ * @short_description: Protocol-independent streaming request interface
+ *
+ * FIXME
+ **/
+
+/**
+ * WebKitSoupRequest:
+ *
+ * FIXME
+ *
+ * Since: 2.30
+ **/
+
+static void webkit_soup_request_initable_interface_init (GInitableIface *initable_interface);
+
+G_DEFINE_TYPE_WITH_CODE (WebKitSoupRequest, webkit_soup_request, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                               webkit_soup_request_initable_interface_init))
+
+enum {
+       PROP_0,
+       PROP_URI,
+       PROP_SESSION
+};
+
+struct _WebKitSoupRequestPrivate {
+       SoupURI *uri;
+       SoupSession *session;
+};
+
+static void
+webkit_soup_request_init (WebKitSoupRequest *request)
+{
+       request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequestPrivate);
+}
+
+static void
+webkit_soup_request_finalize (GObject *object)
+{
+       WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (object);
+
+       if (request->priv->uri)
+               soup_uri_free (request->priv->uri);
+       if (request->priv->session)
+               g_object_unref (request->priv->session);
+
+       G_OBJECT_CLASS (webkit_soup_request_parent_class)->finalize (object);
+}
+
+static void
+webkit_soup_request_set_property (GObject      *object,
+                                 guint prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+       WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (object);
+
+       switch (prop_id) {
+       case PROP_URI:
+               if (request->priv->uri)
+                       soup_uri_free (request->priv->uri);
+               request->priv->uri = g_value_dup_boxed (value);
+               break;
+       case PROP_SESSION:
+               if (request->priv->session)
+                       g_object_unref (request->priv->session);
+               request->priv->session = g_value_dup_object (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+webkit_soup_request_get_property (GObject    *object,
+                                 guint prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+       WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (object);
+
+       switch (prop_id) {
+       case PROP_URI:
+               g_value_set_boxed (value, request->priv->uri);
+               break;
+       case PROP_SESSION:
+               g_value_set_object (value, request->priv->session);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static gboolean
+webkit_soup_request_initable_init (GInitable     *initable,
+                                  GCancellable  *cancellable,
+                                  GError       **error)
+{
+       WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (initable);
+       gboolean ok;
+
+       if (!request->priv->uri) {
+               g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI,
+                            _ ("No URI provided"));
+               return FALSE;
+       }
+
+       ok = WEBKIT_SOUP_REQUEST_GET_CLASS (initable)->
+               check_uri (request, request->priv->uri, error);
+
+       if (!ok && error) {
+               char *uri_string = soup_uri_to_string (request->priv->uri, FALSE);
+               g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI,
+                            _ ("Invalid '%s' URI: %s"),
+                            request->priv->uri->scheme,
+                            uri_string);
+               g_free (uri_string);
+       }
+
+       return ok;
+}
+
+static gboolean
+webkit_soup_request_default_check_uri (WebKitSoupRequest  *request,
+                                      SoupURI      *uri,
+                                      GError      **error)
+{
+       return TRUE;
+}
+
+/* Default implementation: assume the sync implementation doesn't block */
+static void
+webkit_soup_request_default_send_async (WebKitSoupRequest          *request,
+                                       GCancellable         *cancellable,
+                                       GAsyncReadyCallback callback,
+                                       gpointer user_data)
+{
+       GSimpleAsyncResult *simple;
+
+       simple = g_simple_async_result_new (G_OBJECT (request),
+                                           callback, user_data,
+                                           webkit_soup_request_default_send_async);
+       g_simple_async_result_complete_in_idle (simple);
+       g_object_unref (simple);
+}
+
+static GInputStream *
+webkit_soup_request_default_send_finish (WebKitSoupRequest          *request,
+                                        GAsyncResult         *result,
+                                        GError              **error)
+{
+       g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), webkit_soup_request_default_send_async), NULL);
+
+       return webkit_soup_request_send (request, NULL, error);
+}
+
+GInputStream *
+webkit_soup_request_send (WebKitSoupRequest          *request,
+                         GCancellable         *cancellable,
+                         GError              **error)
+{
+       return WEBKIT_SOUP_REQUEST_GET_CLASS (request)->
+               send (request, cancellable, error);
+}
+
+void
+webkit_soup_request_send_async (WebKitSoupRequest          *request,
+                               GCancellable         *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+       WEBKIT_SOUP_REQUEST_GET_CLASS (request)->
+               send_async (request, cancellable, callback, user_data);
+}
+
+GInputStream *
+webkit_soup_request_send_finish (WebKitSoupRequest          *request,
+                                GAsyncResult         *result,
+                                GError              **error)
+{
+       return WEBKIT_SOUP_REQUEST_GET_CLASS (request)->
+               send_finish (request, result, error);
+}
+
+static void
+webkit_soup_request_class_init (WebKitSoupRequestClass *request_class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (request_class);
+
+       g_type_class_add_private (request_class, sizeof (WebKitSoupRequestPrivate));
+
+       request_class->check_uri = webkit_soup_request_default_check_uri;
+       request_class->send_async = webkit_soup_request_default_send_async;
+       request_class->send_finish = webkit_soup_request_default_send_finish;
+
+       object_class->finalize = webkit_soup_request_finalize;
+       object_class->set_property = webkit_soup_request_set_property;
+       object_class->get_property = webkit_soup_request_get_property;
+
+       g_object_class_install_property (
+                object_class, PROP_URI,
+                g_param_spec_boxed (WEBKIT_SOUP_REQUEST_URI,
+                                    "URI",
+                                    "The request URI",
+                                    SOUP_TYPE_URI,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
+                object_class, PROP_SESSION,
+                g_param_spec_object (WEBKIT_SOUP_REQUEST_SESSION,
+                                     "Session",
+                                     "The request's session",
+                                     SOUP_TYPE_SESSION,
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+webkit_soup_request_initable_interface_init (GInitableIface *initable_interface)
+{
+       initable_interface->init = webkit_soup_request_initable_init;
+}
+
+SoupURI *
+webkit_soup_request_get_uri (WebKitSoupRequest *request)
+{
+       return request->priv->uri;
+}
+
+SoupSession *
+webkit_soup_request_get_session (WebKitSoupRequest *request)
+{
+       return request->priv->session;
+}
+
+goffset
+webkit_soup_request_get_content_length (WebKitSoupRequest *request)
+{
+       return WEBKIT_SOUP_REQUEST_GET_CLASS (request)->get_content_length (request);
+}
+
+const char *
+webkit_soup_request_get_content_type (WebKitSoupRequest  *request)
+{
+       return WEBKIT_SOUP_REQUEST_GET_CLASS (request)->get_content_type (request);
+}
+
+#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
+#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
+
+/* Copy&pasted from libsoup's soup-uri.c after applying the patch in
+ * https://bugzilla.gnome.org/show_bug.cgi?id=630540. We need this
+ * instead of soup_uri_decode() as it incorrectly returns NULL for
+ * incorrectly encoded URLs. TODO: remove this when required libsoup
+ * version is bumped out to 2.32.1
+ */
+gchar *
+webkit_soup_request_uri_decoded_copy (const char *part, int length)
+{
+       unsigned char *s, *d;
+        char *decoded = g_strndup (part, length);
+
+        s = d = (unsigned char *)decoded;
+        do {
+                if (*s == '%') {
+                        if (!g_ascii_isxdigit (s[1]) ||
+                            !g_ascii_isxdigit (s[2])) {
+                                *d++ = *s;
+                                continue;
+                        }
+                        *d++ = HEXCHAR (s);
+                        s += 2;
+                } else
+                        *d++ = *s;
+        } while (*s++);
+
+        return decoded;
+}
diff --git a/WebCore/platform/network/soup/cache/soup-request.h b/WebCore/platform/network/soup/cache/soup-request.h
new file mode 100644 (file)
index 0000000..837d8f4
--- /dev/null
@@ -0,0 +1,100 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_REQUEST_H
+#define WEBKIT_SOUP_REQUEST_H 1
+
+#include <libsoup/soup.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_REQUEST            (webkit_soup_request_get_type ())
+#define WEBKIT_SOUP_REQUEST(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequest))
+#define WEBKIT_SOUP_REQUEST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequestClass))
+#define WEBKIT_IS_SOUP_REQUEST(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_REQUEST))
+#define WEBKIT_IS_SOUP_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST))
+#define WEBKIT_SOUP_REQUEST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequestClass))
+
+typedef struct _WebKitSoupRequest WebKitSoupRequest;
+typedef struct _WebKitSoupRequestPrivate WebKitSoupRequestPrivate;
+typedef struct _WebKitSoupRequestClass WebKitSoupRequestClass;
+
+struct _WebKitSoupRequest {
+       GObject parent;
+
+       WebKitSoupRequestPrivate *priv;
+};
+
+struct _WebKitSoupRequestClass {
+       GObjectClass parent;
+
+       gboolean (*check_uri)(WebKitSoupRequest          *req_base,
+                             SoupURI              *uri,
+                             GError              **error);
+
+       GInputStream * (*send)(WebKitSoupRequest          *request,
+                              GCancellable         *cancellable,
+                              GError              **error);
+       void (*send_async)(WebKitSoupRequest          *request,
+                          GCancellable         *cancellable,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data);
+       GInputStream * (*send_finish)(WebKitSoupRequest          *request,
+                                     GAsyncResult         *result,
+                                     GError              **error);
+
+       goffset (*get_content_length)(WebKitSoupRequest   *request);
+       const char *   (*get_content_type)(WebKitSoupRequest   *request);
+};
+
+GType webkit_soup_request_get_type (void);
+
+#define WEBKIT_SOUP_REQUEST_URI     "uri"
+#define WEBKIT_SOUP_REQUEST_SESSION "session"
+
+GInputStream *webkit_soup_request_send (WebKitSoupRequest          *request,
+                                       GCancellable         *cancellable,
+                                       GError              **error);
+void          webkit_soup_request_send_async (WebKitSoupRequest          *request,
+                                             GCancellable         *cancellable,
+                                             GAsyncReadyCallback callback,
+                                             gpointer user_data);
+GInputStream *webkit_soup_request_send_finish (WebKitSoupRequest          *request,
+                                              GAsyncResult         *result,
+                                              GError              **error);
+
+SoupURI      *webkit_soup_request_get_uri (WebKitSoupRequest          *request);
+SoupSession  *webkit_soup_request_get_session (WebKitSoupRequest          *request);
+
+goffset       webkit_soup_request_get_content_length (WebKitSoupRequest          *request);
+const char   *webkit_soup_request_get_content_type (WebKitSoupRequest          *request);
+
+/* Used by WebKitSoupRequestFile and WebKitSoupRequestData. Ideally
+ * should be located in some util file but I'll place it here as it
+ * will be removed with libsoup 2.32.1 that will ship fixed versions
+ * of soup_uri_decode/normalize
+ */
+gchar *webkit_soup_request_uri_decoded_copy (const char *part, int length);
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_REQUEST_H */
diff --git a/WebCore/platform/network/soup/cache/soup-requester.c b/WebCore/platform/network/soup/cache/soup-requester.c
new file mode 100644 (file)
index 0000000..6ce89d9
--- /dev/null
@@ -0,0 +1,188 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-requester.c:
+ *
+ * Copyright (C) 2010, Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "soup-requester.h"
+
+#include "soup-request-data.h"
+#include "soup-request-file.h"
+#include "soup-request-http.h"
+#include <glib/gi18n.h>
+#include <libsoup/soup.h>
+
+struct _WebKitSoupRequesterPrivate {
+       GHashTable *request_types;
+};
+
+#define WEBKIT_SOUP_REQUESTER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequesterPrivate))
+
+G_DEFINE_TYPE (WebKitSoupRequester, webkit_soup_requester, G_TYPE_OBJECT)
+
+static void webkit_soup_requester_init (WebKitSoupRequester *requester)
+{
+       requester->priv = WEBKIT_SOUP_REQUESTER_GET_PRIVATE (requester);
+
+       requester->priv->request_types = 0;
+}
+
+static void finalize (GObject *object)
+{
+       WebKitSoupRequester *requester = WEBKIT_SOUP_REQUESTER (object);
+
+       if (requester->priv->request_types)
+               g_hash_table_destroy (requester->priv->request_types);
+
+       G_OBJECT_CLASS (webkit_soup_requester_parent_class)->finalize (object);
+}
+
+static void webkit_soup_requester_class_init (WebKitSoupRequesterClass *requester_class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (requester_class);
+
+       g_type_class_add_private (requester_class, sizeof (WebKitSoupRequesterPrivate));
+
+       /* virtual method override */
+       object_class->finalize = finalize;
+}
+
+static void init_request_types (WebKitSoupRequesterPrivate *priv)
+{
+       if (priv->request_types)
+               return;
+
+       priv->request_types = g_hash_table_new_full (soup_str_case_hash,
+                                                    soup_str_case_equal,
+                                                    g_free, 0);
+       g_hash_table_insert (priv->request_types, g_strdup ("file"),
+                            GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_FILE));
+       g_hash_table_insert (priv->request_types, g_strdup ("data"),
+                            GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_DATA));
+       g_hash_table_insert (priv->request_types, g_strdup ("http"),
+                            GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_HTTP));
+       g_hash_table_insert (priv->request_types, g_strdup ("https"),
+                            GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_HTTP));
+       g_hash_table_insert (priv->request_types, g_strdup ("ftp"),
+                            GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_FILE));
+}
+
+WebKitSoupRequester *webkit_soup_requester_new (void)
+{
+       return (WebKitSoupRequester *)g_object_new (WEBKIT_TYPE_SOUP_REQUESTER, 0);
+}
+
+WebKitSoupRequest *webkit_soup_requester_request (WebKitSoupRequester *requester, const char *uriString, SoupSession *session, GError **error)
+{
+       SoupURI *uri = NULL;
+       WebKitSoupRequest *req;
+
+       uri = soup_uri_new (uriString);
+       if (!uri) {
+               g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI,
+                            _ ("Could not parse URI '%s'"), uriString);
+               return 0;
+       }
+
+       req = webkit_soup_requester_request_uri (requester, uri, session, error);
+       soup_uri_free (uri);
+       return req;
+}
+
+WebKitSoupRequest *webkit_soup_requester_request_uri (WebKitSoupRequester *requester, SoupURI *uri, SoupSession *session, GError **error)
+{
+       GType requestType;
+
+       g_return_val_if_fail (WEBKIT_IS_SOUP_REQUESTER (requester), 0);
+
+       init_request_types (requester->priv);
+       requestType = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (requester->priv->request_types, uri->scheme));
+       if (!requestType) {
+               g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_UNSUPPORTED_URI_SCHEME,
+                            _ ("Unsupported URI scheme '%s'"), uri->scheme);
+               return 0;
+       }
+
+       if (g_type_is_a (requestType, G_TYPE_INITABLE)) {
+               return (WebKitSoupRequest *)g_initable_new (requestType, 0, error,
+                                                           "uri", uri,
+                                                           "session", session,
+                                                           0);
+       } else {
+               return (WebKitSoupRequest *)g_object_new (requestType,
+                                                         "uri", uri,
+                                                         "session", session,
+                                                         0);
+       }
+}
+
+/* RFC 2396, 3.1 */
+static gboolean
+soup_scheme_is_valid (const char *scheme)
+{
+       if (scheme == NULL ||
+           !g_ascii_isalpha (*scheme))
+               return FALSE;
+
+       scheme++;
+       while (*scheme) {
+               if (!g_ascii_isalpha (*scheme) &&
+                   !g_ascii_isdigit (*scheme) &&
+                   *scheme != '+' &&
+                   *scheme != '-' &&
+                   *scheme != '.')
+                       return FALSE;
+               scheme++;
+       }
+       return TRUE;
+}
+
+void
+webkit_soup_requester_add_protocol (WebKitSoupRequester *requester,
+                                   const char  *scheme,
+                                   GType request_type)
+{
+       g_return_if_fail (WEBKIT_IS_SOUP_REQUESTER (requester));
+       g_return_if_fail (soup_scheme_is_valid (scheme));
+
+       init_request_types (requester->priv);
+       g_hash_table_insert (requester->priv->request_types, g_strdup (scheme),
+                            GSIZE_TO_POINTER (request_type));
+}
+
+void
+webkit_soup_requester_remove_protocol (WebKitSoupRequester *requester,
+                                      const char  *scheme)
+{
+       g_return_if_fail (WEBKIT_IS_SOUP_REQUESTER (requester));
+       g_return_if_fail (soup_scheme_is_valid (scheme));
+
+       init_request_types (requester->priv);
+       g_hash_table_remove (requester->priv->request_types, scheme);
+}
+
+GQuark
+webkit_soup_error_quark (void)
+{
+       static GQuark error;
+       if (!error)
+               error = g_quark_from_static_string ("webkit_soup_error_quark");
+       return error;
+}
diff --git a/WebCore/platform/network/soup/cache/soup-requester.h b/WebCore/platform/network/soup/cache/soup-requester.h
new file mode 100644 (file)
index 0000000..71ff103
--- /dev/null
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_REQUESTER_H
+#define WEBKIT_SOUP_REQUESTER_H 1
+
+#include "soup-request.h"
+#include <libsoup/soup.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_REQUESTER            (webkit_soup_requester_get_type ())
+#define WEBKIT_SOUP_REQUESTER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequester))
+#define WEBKIT_SOUP_REQUESTER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequesterClass))
+#define WEBKIT_IS_SOUP_REQUESTER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_REQUESTER))
+#define WEBKIT_IS_SOUP_REQUESTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), WEBKIT_TYPE_SOUP_REQUESTER))
+#define WEBKIT_SOUP_REQUESTER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequesterClass))
+
+#define WEBKIT_SOUP_ERROR webkit_soup_error_quark ()
+
+typedef enum {
+       WEBKIT_SOUP_ERROR_BAD_URI,
+       WEBKIT_SOUP_ERROR_UNSUPPORTED_URI_SCHEME
+} WebKitSoupError;
+
+typedef struct _WebKitSoupRequester WebKitSoupRequester;
+typedef struct _WebKitSoupRequesterPrivate WebKitSoupRequesterPrivate;
+
+struct _WebKitSoupRequester {
+       GObject parent;
+
+       WebKitSoupRequesterPrivate *priv;
+};
+
+typedef struct {
+       GObjectClass parent_class;
+} WebKitSoupRequesterClass;
+
+GType webkit_soup_requester_get_type (void);
+
+WebKitSoupRequester *webkit_soup_requester_new (void);
+
+GQuark webkit_soup_error_quark (void);
+
+WebKitSoupRequest *webkit_soup_requester_request (WebKitSoupRequester *requester,
+                                                 const char *uriString,
+                                                 SoupSession *session,
+                                                 GError **error);
+
+WebKitSoupRequest *webkit_soup_requester_request_uri (WebKitSoupRequester *requester,
+                                                     SoupURI *uri,
+                                                     SoupSession *session,
+                                                     GError **error);
+
+void webkit_soup_requester_add_protocol (WebKitSoupRequester *requester,
+                                        const char  *scheme,
+                                        GType request_type);
+
+void webkit_soup_requester_remove_protocol (WebKitSoupRequester *requester,
+                                           const char  *scheme);
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_REQUESTER_H */
diff --git a/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h b/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h
new file mode 100644 (file)
index 0000000..8af8de2
--- /dev/null
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cache-private.h:
+ *
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_CACHE_PRIVATE_H
+#define WEBKIT_SOUP_CACHE_PRIVATE_H 1
+
+#include "soup-cache.h"
+#include <libsoup/soup-message.h>
+
+G_BEGIN_DECLS
+
+WebKitSoupCacheResponse webkit_soup_cache_has_response (WebKitSoupCache   *cache,
+                                                       SoupMessage *msg);
+GInputStream *webkit_soup_cache_send_response (WebKitSoupCache   *cache,
+                                              SoupMessage *msg);
+WebKitSoupCacheability  webkit_soup_cache_get_cacheability (WebKitSoupCache   *cache,
+                                                           SoupMessage *msg);
+SoupMessage *webkit_soup_cache_generate_conditional_request (WebKitSoupCache   *cache,
+                                                            SoupMessage *original);
+
+G_END_DECLS
+
+#endif /* WEBKIT_SOUP_CACHE_PRIVATE_H */
diff --git a/WebCore/platform/network/soup/cache/webkit/soup-cache.c b/WebCore/platform/network/soup/cache/webkit/soup-cache.c
new file mode 100644 (file)
index 0000000..a9446fe
--- /dev/null
@@ -0,0 +1,1653 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cache.c
+ *
+ * Copyright (C) 2009, 2010 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/* TODO:
+ * - Need to hook the feature in the sync SoupSession.
+ * - Need more tests.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-cache.h"
+#include "soup-cache-private.h"
+#include <libsoup/soup.h>
+#include <gio/gio.h>
+#include <stdlib.h>
+
+static SoupSessionFeatureInterface *webkit_soup_cache_default_feature_interface;
+static void webkit_soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+
+#define DEFAULT_MAX_SIZE 50 * 1024 * 1024
+#define MAX_ENTRY_DATA_PERCENTAGE 10 /* Percentage of the total size
+                                       of the cache that can be
+                                       filled by a single entry */
+
+typedef struct _WebKitSoupCacheEntry {
+       char *key;
+       char *filename;
+       guint freshness_lifetime;
+       gboolean must_revalidate;
+       GString *data;
+       gsize pos;
+       gsize length;
+       time_t corrected_initial_age;
+       time_t response_time;
+       gboolean writing;
+       gboolean dirty;
+       gboolean got_body;
+       gboolean being_validated;
+       SoupMessageHeaders *headers;
+       GOutputStream *stream;
+       GError *error;
+       guint hits;
+       GCancellable *cancellable;
+} WebKitSoupCacheEntry;
+
+struct _WebKitSoupCachePrivate {
+       char *cache_dir;
+       GHashTable *cache;
+       guint n_pending;
+       SoupSession *session;
+       WebKitSoupCacheType cache_type;
+       guint size;
+       guint max_size;
+       guint max_entry_data_size; /* Computed value. Here for performance reasons */
+       GList *lru_start;
+};
+
+typedef struct {
+       WebKitSoupCache *cache;
+       WebKitSoupCacheEntry *entry;
+       SoupMessage *msg;
+       gulong got_chunk_handler;
+       gulong got_body_handler;
+       gulong restarted_handler;
+} WebKitSoupCacheWritingFixture;
+
+enum {
+       PROP_0,
+       PROP_CACHE_DIR,
+       PROP_CACHE_TYPE
+};
+
+#define WEBKIT_SOUP_CACHE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCachePrivate))
+
+G_DEFINE_TYPE_WITH_CODE (WebKitSoupCache, webkit_soup_cache, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+                                               webkit_soup_cache_session_feature_init))
+
+static gboolean webkit_soup_cache_entry_remove (WebKitSoupCache *cache, WebKitSoupCacheEntry *entry);
+static void make_room_for_new_entry (WebKitSoupCache *cache, guint length_to_add);
+static gboolean cache_accepts_entries_of_size (WebKitSoupCache *cache, guint length_to_add);
+
+static WebKitSoupCacheability
+get_cacheability (WebKitSoupCache *cache, SoupMessage *msg)
+{
+       WebKitSoupCacheability cacheability;
+       const char *cache_control;
+
+       /* 1. The request method must be cacheable */
+       if (msg->method == SOUP_METHOD_GET)
+               cacheability = WEBKIT_SOUP_CACHE_CACHEABLE;
+       else if (msg->method == SOUP_METHOD_HEAD ||
+                msg->method == SOUP_METHOD_TRACE ||
+                msg->method == SOUP_METHOD_CONNECT)
+               return WEBKIT_SOUP_CACHE_UNCACHEABLE;
+       else
+               return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES);
+
+       cache_control = soup_message_headers_get (msg->response_headers, "Cache-Control");
+       if (cache_control) {
+               GHashTable *hash;
+               WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
+
+               hash = soup_header_parse_param_list (cache_control);
+
+               /* Shared caches MUST NOT store private resources */
+               if (priv->cache_type == WEBKIT_SOUP_CACHE_SHARED) {
+                       if (g_hash_table_lookup_extended (hash, "private", NULL, NULL)) {
+                               soup_header_free_param_list (hash);
+                               return WEBKIT_SOUP_CACHE_UNCACHEABLE;
+                       }
+               }
+
+               /* 2. The 'no-store' cache directive does not appear in the
+                * headers
+                */
+               if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) {
+                       soup_header_free_param_list (hash);
+                       return WEBKIT_SOUP_CACHE_UNCACHEABLE;
+               }
+
+               /* This does not appear in section 2.1, but I think it makes
+                * sense to check it too?
+                */
+               if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) {
+                       soup_header_free_param_list (hash);
+                       return WEBKIT_SOUP_CACHE_UNCACHEABLE;
+               }
+       }
+
+       switch (msg->status_code) {
+       case SOUP_STATUS_PARTIAL_CONTENT:
+               /* We don't cache partial responses, but they only
+                * invalidate cached full responses if the headers
+                * don't match.
+                */
+               cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE;
+               break;
+
+       case SOUP_STATUS_NOT_MODIFIED:
+               /* A 304 response validates an existing cache entry */
+               cacheability = WEBKIT_SOUP_CACHE_VALIDATES;
+               break;
+
+       case SOUP_STATUS_MULTIPLE_CHOICES:
+       case SOUP_STATUS_MOVED_PERMANENTLY:
+       case SOUP_STATUS_GONE:
+               /* FIXME: cacheable unless indicated otherwise */
+               cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE;
+               break;
+
+       case SOUP_STATUS_FOUND:
+       case SOUP_STATUS_TEMPORARY_REDIRECT:
+               /* FIXME: cacheable if explicitly indicated */
+               cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE;
+               break;
+
+       case SOUP_STATUS_SEE_OTHER:
+       case SOUP_STATUS_FORBIDDEN:
+       case SOUP_STATUS_NOT_FOUND:
+       case SOUP_STATUS_METHOD_NOT_ALLOWED:
+               return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES);
+
+       default:
+               /* Any 5xx status or any 4xx status not handled above
+                * is uncacheable but doesn't break the cache.
+                */
+               if ((msg->status_code >= SOUP_STATUS_BAD_REQUEST &&
+                    msg->status_code <= SOUP_STATUS_FAILED_DEPENDENCY) ||
+                   msg->status_code >= SOUP_STATUS_INTERNAL_SERVER_ERROR)
+                       return WEBKIT_SOUP_CACHE_UNCACHEABLE;
+
+               /* An unrecognized 2xx, 3xx, or 4xx response breaks
+                * the cache.
+                */
+               if ((msg->status_code > SOUP_STATUS_PARTIAL_CONTENT &&
+                    msg->status_code < SOUP_STATUS_MULTIPLE_CHOICES) ||
+                   (msg->status_code > SOUP_STATUS_TEMPORARY_REDIRECT &&
+                    msg->status_code < SOUP_STATUS_INTERNAL_SERVER_ERROR))
+                       return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES);
+               break;
+       }
+
+       return cacheability;
+}
+
+static void
+webkit_soup_cache_entry_free (WebKitSoupCacheEntry *entry, gboolean purge)
+{
+       if (purge) {
+               GFile *file = g_file_new_for_path (entry->filename);
+               g_file_delete (file, NULL, NULL);
+               g_object_unref (file);
+       }
+
+       g_free (entry->filename);
+       entry->filename = NULL;
+       g_free (entry->key);
+       entry->key = NULL;
+
+       if (entry->headers) {
+               soup_message_headers_free (entry->headers);
+               entry->headers = NULL;
+       }
+
+       if (entry->data) {
+               g_string_free (entry->data, TRUE);
+               entry->data = NULL;
+       }
+       if (entry->error) {
+               g_error_free (entry->error);
+               entry->error = NULL;
+       }
+       if (entry->cancellable) {
+               g_object_unref (entry->cancellable);
+               entry->cancellable = NULL;
+       }
+
+       g_slice_free (WebKitSoupCacheEntry, entry);
+}
+
+static void
+copy_headers (const char *name, const char *value, SoupMessageHeaders *headers)
+{
+       soup_message_headers_append (headers, name, value);
+}
+
+static void
+update_headers (const char *name, const char *value, SoupMessageHeaders *headers)
+{
+       if (soup_message_headers_get (headers, name))
+               soup_message_headers_replace (headers, name, value);
+       else
+               soup_message_headers_append (headers, name, value);
+}
+
+static guint
+webkit_soup_cache_entry_get_current_age (WebKitSoupCacheEntry *entry)
+{
+       time_t now = time (NULL);
+       time_t resident_time;
+
+       resident_time = now - entry->response_time;
+       return entry->corrected_initial_age + resident_time;
+}
+
+static gboolean
+webkit_soup_cache_entry_is_fresh_enough (WebKitSoupCacheEntry *entry, int min_fresh)
+{
+       unsigned limit = (min_fresh == -1) ? webkit_soup_cache_entry_get_current_age (entry) : min_fresh;
+       return entry->freshness_lifetime > limit;
+}
+
+static char *
+soup_message_get_cache_key (SoupMessage *msg)
+{
+       SoupURI *uri = soup_message_get_uri (msg);
+       return soup_uri_to_string (uri, FALSE);
+}
+
+static void
+webkit_soup_cache_entry_set_freshness (WebKitSoupCacheEntry *entry, SoupMessage *msg, WebKitSoupCache *cache)
+{
+       const char *cache_control;
+       const char *expires, *date, *last_modified;
+       GHashTable *hash;
+
+       hash = NULL;
+
+       cache_control = soup_message_headers_get (entry->headers, "Cache-Control");
+       if (cache_control) {
+               const char *max_age, *s_maxage;
+               gint64 freshness_lifetime = 0;
+               WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
+
+               hash = soup_header_parse_param_list (cache_control);
+
+               /* Should we re-validate the entry when it goes stale */
+               entry->must_revalidate = (gboolean)g_hash_table_lookup (hash, "must-revalidate");
+
+               /* Section 2.3.1 */
+               if (priv->cache_type == WEBKIT_SOUP_CACHE_SHARED) {
+                       s_maxage = g_hash_table_lookup (hash, "s-maxage");
+                       if (s_maxage) {
+                               freshness_lifetime = g_ascii_strtoll (s_maxage, NULL, 10);
+                               if (freshness_lifetime) {
+                                       /* Implies proxy-revalidate. TODO: is it true? */
+                                       entry->must_revalidate = TRUE;
+                                       soup_header_free_param_list (hash);
+                                       return;
+                               }
+                       }
+               }
+
+               /* If 'max-age' cache directive is present, use that */
+               max_age = g_hash_table_lookup (hash, "max-age");
+               if (max_age)
+                       freshness_lifetime = g_ascii_strtoll (max_age, NULL, 10);
+
+               if (freshness_lifetime) {
+                       entry->freshness_lifetime = (guint)MIN (freshness_lifetime, G_MAXUINT32);
+                       soup_header_free_param_list (hash);
+                       return;
+               }
+       }
+
+       if (hash != NULL)
+               soup_header_free_param_list (hash);
+
+       /* If the 'Expires' response header is present, use its value
+        * minus the value of the 'Date' response header
+        */
+       expires = soup_message_headers_get (entry->headers, "Expires");
+       date = soup_message_headers_get (entry->headers, "Date");
+       if (expires && date) {
+               SoupDate *expires_d, *date_d;
+               time_t expires_t, date_t;
+
+               expires_d = soup_date_new_from_string (expires);
+               if (expires_d) {
+                       date_d = soup_date_new_from_string (date);
+
+                       expires_t = soup_date_to_time_t (expires_d);
+                       date_t = soup_date_to_time_t (date_d);
+
+                       soup_date_free (expires_d);
+                       soup_date_free (date_d);
+
+                       if (expires_t && date_t) {
+                               entry->freshness_lifetime = (guint)MAX (expires_t - date_t, 0);
+                               return;
+                       }
+               } else {
+                       /* If Expires is not a valid date we should
+                          treat it as already expired, see section
+                          3.3 */
+                       entry->freshness_lifetime = 0;
+                       return;
+               }
+       }
+
+       /* Otherwise an heuristic may be used */
+
+       /* Heuristics MUST NOT be used with these status codes
+          (section 2.3.1.1) */
+       if (msg->status_code != SOUP_STATUS_OK &&
+           msg->status_code != SOUP_STATUS_NON_AUTHORITATIVE &&
+           msg->status_code != SOUP_STATUS_PARTIAL_CONTENT &&
+           msg->status_code != SOUP_STATUS_MULTIPLE_CHOICES &&
+           msg->status_code != SOUP_STATUS_MOVED_PERMANENTLY &&
+           msg->status_code != SOUP_STATUS_GONE)
+               goto expire;
+
+       /* TODO: attach warning 113 if response's current_age is more
+          than 24h (section 2.3.1.1) when using heuristics */
+
+       /* Last-Modified based heuristic */
+       last_modified = soup_message_headers_get (entry->headers, "Last-Modified");
+       if (last_modified) {
+               SoupDate *soup_date;
+               time_t now, last_modified_t;
+
+               soup_date = soup_date_new_from_string (last_modified);
+               last_modified_t = soup_date_to_time_t (soup_date);
+               now = time (NULL);
+
+#define HEURISTIC_FACTOR 0.1 /* From Section 2.3.1.1 */
+
+               entry->freshness_lifetime = MAX (0, (now - last_modified_t) * HEURISTIC_FACTOR);
+               soup_date_free (soup_date);
+       }
+
+       return;
+
+ expire:
+       /* If all else fails, make the entry expire immediately */
+       entry->freshness_lifetime = 0;
+}
+
+static WebKitSoupCacheEntry *
+webkit_soup_cache_entry_new (WebKitSoupCache *cache, SoupMessage *msg, time_t request_time, time_t response_time)
+{
+       WebKitSoupCacheEntry *entry;
+       SoupMessageHeaders *headers;
+       const char *date;
+       char *md5;
+
+       entry = g_slice_new0 (WebKitSoupCacheEntry);
+       entry->dirty = FALSE;
+       entry->writing = FALSE;
+       entry->got_body = FALSE;
+       entry->being_validated = FALSE;
+       entry->data = g_string_new (NULL);
+       entry->pos = 0;
+       entry->error = NULL;
+
+       /* key & filename */
+       entry->key = soup_message_get_cache_key (msg);
+       md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, entry->key, -1);
+       entry->filename = g_build_filename (cache->priv->cache_dir, md5, NULL);
+       g_free (md5);
+
+       /* Headers */
+       headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
+       soup_message_headers_foreach (msg->response_headers,
+                                     (SoupMessageHeadersForeachFunc)copy_headers,
+                                     headers);
+       entry->headers = headers;
+
+       /* LRU list */
+       entry->hits = 0;
+
+       /* Section 2.3.1, Freshness Lifetime */
+       webkit_soup_cache_entry_set_freshness (entry, msg, cache);
+
+       /* Section 2.3.2, Calculating Age */
+       date = soup_message_headers_get (entry->headers, "Date");
+
+       if (date) {
+               SoupDate *soup_date;
+               const char *age;
+               time_t date_value, apparent_age, corrected_received_age, response_delay, age_value = 0;
+
+               soup_date = soup_date_new_from_string (date);
+               date_value = soup_date_to_time_t (soup_date);
+               soup_date_free (soup_date);
+
+               age = soup_message_headers_get (entry->headers, "Age");
+               if (age)
+                       age_value = g_ascii_strtoll (age, NULL, 10);
+
+               entry->response_time = response_time;
+               apparent_age = MAX (0, entry->response_time - date_value);
+               corrected_received_age = MAX (apparent_age, age_value);
+               response_delay = entry->response_time - request_time;
+               entry->corrected_initial_age = corrected_received_age + response_delay;
+       } else {
+               /* Is this correct ? */
+               entry->corrected_initial_age = time (NULL);
+       }
+
+       return entry;
+}
+
+static void
+webkit_soup_cache_writing_fixture_free (WebKitSoupCacheWritingFixture *fixture)
+{
+       /* Free fixture. And disconnect signals, we don't want to
+          listen to more SoupMessage events as we're finished with
+          this resource */
+       if (g_signal_handler_is_connected (fixture->msg, fixture->got_chunk_handler))
+               g_signal_handler_disconnect (fixture->msg, fixture->got_chunk_handler);
+       if (g_signal_handler_is_connected (fixture->msg, fixture->got_body_handler))
+               g_signal_handler_disconnect (fixture->msg, fixture->got_body_handler);
+       if (g_signal_handler_is_connected (fixture->msg, fixture->restarted_handler))
+               g_signal_handler_disconnect (fixture->msg, fixture->restarted_handler);
+       g_object_unref (fixture->msg);
+       g_object_unref (fixture->cache);
+       g_slice_free (WebKitSoupCacheWritingFixture, fixture);
+}
+
+static void
+close_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture)
+{
+       WebKitSoupCacheEntry *entry = fixture->entry;
+       WebKitSoupCache *cache = fixture->cache;
+       GOutputStream *stream = G_OUTPUT_STREAM (source);
+       goffset content_length;
+
+       g_warn_if_fail (entry->error == NULL);
+
+       /* FIXME: what do we do on error ? */
+
+       if (stream) {
+               g_output_stream_close_finish (stream, result, NULL);
+               g_object_unref (stream);
+       }
+       entry->stream = NULL;
+
+       content_length = soup_message_headers_get_content_length (entry->headers);
+
+       /* If the process was cancelled, then delete the entry from
+          the cache. Do it also if the size of a chunked resource is
+          too much for the cache */
+       if (g_cancellable_is_cancelled (entry->cancellable)) {
+               entry->dirty = FALSE;
+               webkit_soup_cache_entry_remove (cache, entry);
+               webkit_soup_cache_entry_free (entry, TRUE);
+               entry = NULL;
+       } else if ((soup_message_headers_get_encoding (entry->headers) == SOUP_ENCODING_CHUNKED) ||
+                  entry->length != content_length) {
+               /** Two options here:
+                *
+                * 1. "chunked" data, entry was temporarily added to
+                * cache (as content-length is 0) and now that we have
+                * the actual size we have to evaluate if we want it
+                * in the cache or not
+                *
+                * 2. Content-Length has a different value than actual
+                * length, means that the content was encoded for
+                * transmission (typically compressed) and thus we
+                * have to substract the content-length value that was
+                * added to the cache and add the unencoded length
+                **/
+               gint length_to_add = entry->length - content_length;
+
+               /* Make room in cache if needed */
+               if (cache_accepts_entries_of_size (cache, length_to_add)) {
+                       make_room_for_new_entry (cache, length_to_add);
+
+                       cache->priv->size += length_to_add;
+               } else {
+                       entry->dirty = FALSE;
+                       webkit_soup_cache_entry_remove (cache, entry);
+                       webkit_soup_cache_entry_free (entry, TRUE);
+                       entry = NULL;
+               }
+       }
+
+       if (entry) {
+               /* Get rid of the GString in memory for the resource now */
+               if (entry->data) {
+                       g_string_free (entry->data, TRUE);
+                       entry->data = NULL;
+               }
+
+               entry->dirty = FALSE;
+               entry->writing = FALSE;
+               entry->got_body = FALSE;
+               entry->pos = 0;
+
+               g_object_unref (entry->cancellable);
+               entry->cancellable = NULL;
+       }
+
+       cache->priv->n_pending--;
+
+       /* Frees */
+       webkit_soup_cache_writing_fixture_free (fixture);
+}
+
+static void
+write_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture)
+{
+       GOutputStream *stream = G_OUTPUT_STREAM (source);
+       GError *error = NULL;
+       gssize write_size;
+       WebKitSoupCacheEntry *entry = fixture->entry;
+
+       if (g_cancellable_is_cancelled (entry->cancellable)) {
+               g_output_stream_close_async (stream,
+                                            G_PRIORITY_LOW,
+                                            entry->cancellable,
+                                            (GAsyncReadyCallback)close_ready_cb,
+                                            fixture);
+               return;
+       }
+
+       write_size = g_output_stream_write_finish (stream, result, &error);
+       if (write_size <= 0 || error) {
+               if (error)
+                       entry->error = error;
+               g_output_stream_close_async (stream,
+                                            G_PRIORITY_LOW,
+                                            entry->cancellable,
+                                            (GAsyncReadyCallback)close_ready_cb,
+                                            fixture);
+               /* FIXME: We should completely stop caching the
+                  resource at this point */
+       } else {
+               entry->pos += write_size;
+
+               /* Are we still writing and is there new data to write
+                  already ? */
+               if (entry->data && entry->pos < entry->data->len) {
+                       g_output_stream_write_async (entry->stream,
+                                                    entry->data->str + entry->pos,
+                                                    entry->data->len - entry->pos,
+                                                    G_PRIORITY_LOW,
+                                                    entry->cancellable,
+                                                    (GAsyncReadyCallback)write_ready_cb,
+                                                    fixture);
+               } else {
+                       entry->writing = FALSE;
+
+                       if (entry->got_body) {
+                               /* If we already received 'got-body'
+                                  and we have written all the data,
+                                  we can close the stream */
+                               g_output_stream_close_async (entry->stream,
+                                                            G_PRIORITY_LOW,
+                                                            entry->cancellable,
+                                                            (GAsyncReadyCallback)close_ready_cb,
+                                                            fixture);
+                       }
+               }
+       }
+}
+
+static void
+msg_got_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, WebKitSoupCacheWritingFixture *fixture)
+{
+       WebKitSoupCacheEntry *entry = fixture->entry;
+
+       g_return_if_fail (chunk->data && chunk->length);
+       g_return_if_fail (entry);
+
+       /* Ignore this if the writing or appending was cancelled */
+       if (!g_cancellable_is_cancelled (entry->cancellable)) {
+               g_string_append_len (entry->data, chunk->data, chunk->length);
+               entry->length = entry->data->len;
+
+               if (!cache_accepts_entries_of_size (fixture->cache, entry->length)) {
+                       /* Quickly cancel the caching of the resource */
+                       g_cancellable_cancel (entry->cancellable);
+               }
+       }
+
+       /* FIXME: remove the error check when we cancel the caching at
+          the first write error */
+       /* Only write if the entry stream is ready */
+       if (entry->writing == FALSE && entry->error == NULL && entry->stream) {
+               GString *data = entry->data;
+               entry->writing = TRUE;
+               g_output_stream_write_async (entry->stream,
+                                            data->str + entry->pos,
+                                            data->len - entry->pos,
+                                            G_PRIORITY_LOW,
+                                            entry->cancellable,
+                                            (GAsyncReadyCallback)write_ready_cb,
+                                            fixture);
+       }
+}
+
+static void
+msg_got_body_cb (SoupMessage *msg, WebKitSoupCacheWritingFixture *fixture)
+{
+       WebKitSoupCacheEntry *entry = fixture->entry;
+       g_return_if_fail (entry);
+
+       entry->got_body = TRUE;
+
+       if (!entry->stream && entry->pos != entry->length)
+               /* The stream is not ready to be written but we still
+                  have data to write, we'll write it when the stream
+                  is opened for writing */
+               return;
+
+
+       if (entry->pos != entry->length) {
+               /* If we still have data to write, write it,
+                  write_ready_cb will close the stream */
+               if (entry->writing == FALSE && entry->error == NULL && entry->stream) {
+                       g_output_stream_write_async (entry->stream,
+                                                    entry->data->str + entry->pos,
+                                                    entry->data->len - entry->pos,
+                                                    G_PRIORITY_LOW,
+                                                    entry->cancellable,
+                                                    (GAsyncReadyCallback)write_ready_cb,
+                                                    fixture);
+               }
+               return;
+       }
+
+       if (entry->stream && !entry->writing)
+               g_output_stream_close_async (entry->stream,
+                                            G_PRIORITY_LOW,
+                                            entry->cancellable,
+                                            (GAsyncReadyCallback)close_ready_cb,
+                                            fixture);
+}
+
+static gboolean
+webkit_soup_cache_entry_remove (WebKitSoupCache *cache, WebKitSoupCacheEntry *entry)
+{
+       GList *lru_item;
+
+       /* if (entry->dirty && !g_cancellable_is_cancelled (entry->cancellable)) { */
+       if (entry->dirty) {
+               g_cancellable_cancel (entry->cancellable);
+               return FALSE;
+       }
+
+       g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache));
+
+       /* Remove from cache */
+       if (!g_hash_table_remove (cache->priv->cache, entry->key))
+               return FALSE;
+
+       /* Remove from LRU */
+       lru_item = g_list_find (cache->priv->lru_start, entry);
+       cache->priv->lru_start = g_list_delete_link (cache->priv->lru_start, lru_item);
+
+       /* Adjust cache size */
+       cache->priv->size -= entry->length;
+
+       g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache));
+
+       return TRUE;
+}
+
+static gint
+lru_compare_func (gconstpointer a, gconstpointer b)
+{
+       WebKitSoupCacheEntry *entry_a = (WebKitSoupCacheEntry *)a;
+       WebKitSoupCacheEntry *entry_b = (WebKitSoupCacheEntry *)b;
+
+       /** The rationale of this sorting func is
+        *
+        * 1. sort by hits -> LRU algorithm, then
+        *
+        * 2. sort by freshness lifetime, we better discard first
+        * entries that are close to expire
+        *
+        * 3. sort by size, replace first small size resources as they
+        * are cheaper to download
+        **/
+
+       /* Sort by hits */
+       if (entry_a->hits != entry_b->hits)
+               return entry_a->hits - entry_b->hits;
+
+       /* Sort by freshness_lifetime */
+       if (entry_a->freshness_lifetime != entry_b->freshness_lifetime)
+               return entry_a->freshness_lifetime - entry_b->freshness_lifetime;
+
+       /* Sort by size */
+       return entry_a->length - entry_b->length;
+}
+
+static gboolean
+cache_accepts_entries_of_size (WebKitSoupCache *cache, guint length_to_add)
+{
+       /* We could add here some more heuristics. TODO: review how
+          this is done by other HTTP caches */
+
+       return length_to_add <= cache->priv->max_entry_data_size;
+}
+
+static void
+make_room_for_new_entry (WebKitSoupCache *cache, guint length_to_add)
+{
+       GList *lru_entry = cache->priv->lru_start;
+
+       /* Check that there is enough room for the new entry. This is
+          an approximation as we're not working out the size of the
+          cache file or the size of the headers for performance
+          reasons. TODO: check if that would be really that expensive */
+
+       while (lru_entry &&
+              (length_to_add + cache->priv->size > cache->priv->max_size)) {
+               WebKitSoupCacheEntry *old_entry = (WebKitSoupCacheEntry *)lru_entry->data;
+
+               /* Discard entries. Once cancelled resources will be
+                * freed in close_ready_cb
+                */
+               if (webkit_soup_cache_entry_remove (cache, old_entry)) {
+                       webkit_soup_cache_entry_free (old_entry, TRUE);
+                       lru_entry = cache->priv->lru_start;
+               } else
+                       lru_entry = g_list_next (lru_entry);
+       }
+}
+
+static gboolean
+webkit_soup_cache_entry_insert_by_key (WebKitSoupCache *cache,
+                                      const char *key,
+                                      WebKitSoupCacheEntry *entry,
+                                      gboolean sort)
+{
+       guint length_to_add = 0;
+
+       if (soup_message_headers_get_encoding (entry->headers) != SOUP_ENCODING_CHUNKED)
+               length_to_add = soup_message_headers_get_content_length (entry->headers);
+
+       /* Check if we are going to store the resource depending on its size */
+       if (length_to_add) {
+               if (!cache_accepts_entries_of_size (cache, length_to_add))
+                       return FALSE;
+
+               /* Make room for new entry if needed */
+               make_room_for_new_entry (cache, length_to_add);
+       }
+
+       g_hash_table_insert (cache->priv->cache, g_strdup (key), entry);
+
+       /* Compute new cache size */
+       cache->priv->size += length_to_add;
+
+       /* Update LRU */
+       if (sort)
+               cache->priv->lru_start = g_list_insert_sorted (cache->priv->lru_start, entry, lru_compare_func);
+       else
+               cache->priv->lru_start = g_list_prepend (cache->priv->lru_start, entry);
+
+       g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache));
+
+       return TRUE;
+}
+
+static void
+msg_restarted_cb (SoupMessage *msg, WebKitSoupCacheEntry *entry)
+{
+       /* FIXME: What should we do here exactly? */
+}
+
+static void
+append_to_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture)
+{
+       GFile *file = (GFile *)source;
+       GOutputStream *stream;
+       WebKitSoupCacheEntry *entry = fixture->entry;
+
+       stream = (GOutputStream *)g_file_append_to_finish (file, result, &entry->error);
+
+       if (g_cancellable_is_cancelled (entry->cancellable) || entry->error) {
+               fixture->cache->priv->n_pending--;
+               entry->dirty = FALSE;
+               webkit_soup_cache_entry_remove (fixture->cache, entry);
+               webkit_soup_cache_entry_free (entry, TRUE);
+               webkit_soup_cache_writing_fixture_free (fixture);
+               return;
+       }
+
+       entry->stream = g_object_ref (stream);
+       g_object_unref (file);
+
+       /* If we already got all the data we have to initiate the
+          writing here, since we won't get more 'got-chunk'
+          signals */
+       if (entry->got_body) {
+               GString *data = entry->data;
+
+               /* It could happen that reading the data from server
+                  was completed before this happens. In that case
+                  there is no data */
+               if (data) {
+                       entry->writing = TRUE;
+                       g_output_stream_write_async (entry->stream,
+                                                    data->str + entry->pos,
+                                                    data->len - entry->pos,
+                                                    G_PRIORITY_LOW,
+                                                    entry->cancellable,
+                                                    (GAsyncReadyCallback)write_ready_cb,
+                                                    fixture);
+               }
+       }
+}
+
+typedef struct {
+       time_t request_time;
+       SoupSessionFeature *feature;
+       gulong got_headers_handler;
+} RequestHelper;
+
+static void
+msg_got_headers_cb (SoupMessage *msg, gpointer user_data)
+{
+       WebKitSoupCache *cache;
+       WebKitSoupCacheability cacheable;
+       RequestHelper *helper;
+       time_t request_time, response_time;
+
+       response_time = time (NULL);
+
+       helper = (RequestHelper *)user_data;
+       cache = WEBKIT_SOUP_CACHE (helper->feature);
+       request_time = helper->request_time;
+       g_signal_handlers_disconnect_by_func (msg, msg_got_headers_cb, user_data);
+       g_slice_free (RequestHelper, helper);
+
+       cacheable = webkit_soup_cache_get_cacheability (cache, msg);
+
+       if (cacheable & WEBKIT_SOUP_CACHE_CACHEABLE) {
+               WebKitSoupCacheEntry *entry;
+               char *key;
+               GFile *file;
+               WebKitSoupCacheWritingFixture *fixture;
+
+               /* Check if we are already caching this resource */
+               key = soup_message_get_cache_key (msg);
+               entry = g_hash_table_lookup (cache->priv->cache, key);
+               g_free (key);
+
+               if (entry && entry->dirty)
+                       return;
+
+               /* Create a new entry, deleting any old one if present */
+               if (entry) {
+                       webkit_soup_cache_entry_remove (cache, entry);
+                       webkit_soup_cache_entry_free (entry, TRUE);
+               }
+
+               entry = webkit_soup_cache_entry_new (cache, msg, request_time, response_time);
+               entry->hits = 1;
+
+               /* Do not continue if it can not be stored */
+               if (!webkit_soup_cache_entry_insert_by_key (cache, (const gchar *)entry->key, entry, TRUE)) {
+                       webkit_soup_cache_entry_free (entry, TRUE);
+                       return;
+               }
+
+               fixture = g_slice_new0 (WebKitSoupCacheWritingFixture);
+               fixture->cache = g_object_ref (cache);
+               fixture->entry = entry;
+               fixture->msg = g_object_ref (msg);
+
+               /* We connect now to these signals and buffer the data
+                  if it comes before the file is ready for writing */
+               fixture->got_chunk_handler =
+                       g_signal_connect (msg, "got-chunk", G_CALLBACK (msg_got_chunk_cb), fixture);
+               fixture->got_body_handler =
+                       g_signal_connect (msg, "got-body", G_CALLBACK (msg_got_body_cb), fixture);
+               fixture->restarted_handler =
+                       g_signal_connect (msg, "restarted", G_CALLBACK (msg_restarted_cb), entry);
+
+               /* Prepare entry */
+               file = g_file_new_for_path (entry->filename);
+               cache->priv->n_pending++;
+
+               entry->dirty = TRUE;
+               entry->cancellable = g_cancellable_new ();
+               g_file_append_to_async (file, 0,
+                                       G_PRIORITY_LOW, entry->cancellable,
+                                       (GAsyncReadyCallback)append_to_ready_cb,
+                                       fixture);
+       } else if (cacheable & WEBKIT_SOUP_CACHE_INVALIDATES) {
+               char *key;
+               WebKitSoupCacheEntry *entry;
+
+               key = soup_message_get_cache_key (msg);
+               entry = g_hash_table_lookup (cache->priv->cache, key);
+               g_free (key);
+
+               if (entry) {
+                       if (webkit_soup_cache_entry_remove (cache, entry))
+                               webkit_soup_cache_entry_free (entry, TRUE);
+               }
+       } else if (cacheable & WEBKIT_SOUP_CACHE_VALIDATES) {
+               char *key;
+               WebKitSoupCacheEntry *entry;
+
+               key = soup_message_get_cache_key (msg);
+               entry = g_hash_table_lookup (cache->priv->cache, key);
+               g_free (key);
+
+               g_return_if_fail (entry);
+
+               entry->being_validated = FALSE;
+
+               /* We update the headers of the existing cache item,
+                  plus its age */
+               soup_message_headers_foreach (msg->response_headers,
+                                             (SoupMessageHeadersForeachFunc)update_headers,
+                                             entry->headers);
+               webkit_soup_cache_entry_set_freshness (entry, msg, cache);
+       }
+}
+
+GInputStream *
+webkit_soup_cache_send_response (WebKitSoupCache *cache, SoupMessage *msg)
+{
+       char *key;
+       WebKitSoupCacheEntry *entry;
+       char *current_age;
+       GInputStream *stream = NULL;
+       GFile *file;
+
+       g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), NULL);
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+       key = soup_message_get_cache_key (msg);
+       entry = g_hash_table_lookup (cache->priv->cache, key);
+       g_return_val_if_fail (entry, NULL);
+
+       /* If we are told to send a response from cache any validation
+          in course is over by now */
+       entry->being_validated = FALSE;
+
+       /* Headers */
+       soup_message_headers_foreach (entry->headers,
+                                     (SoupMessageHeadersForeachFunc)update_headers,
+                                     msg->response_headers);
+
+       /* Add 'Age' header with the current age */
+       current_age = g_strdup_printf ("%d", webkit_soup_cache_entry_get_current_age (entry));
+       soup_message_headers_replace (msg->response_headers,
+                                     "Age",
+                                     current_age);
+       g_free (current_age);
+
+       /* TODO: the original idea was to save reads, but current code
+          assumes that a stream is always returned. Need to reach
+          some agreement here. Also we have to handle the situation
+          were the file was no longer there (for example files
+          removed without notifying the cache */
+       file = g_file_new_for_path (entry->filename);
+       stream = (GInputStream *)g_file_read (file, NULL, NULL);
+
+       return stream;
+}
+
+static void
+request_started (SoupSessionFeature *feature, SoupSession *session,
+                SoupMessage *msg, SoupSocket *socket)
+{
+       RequestHelper *helper = g_slice_new0 (RequestHelper);
+       helper->request_time = time (NULL);
+       helper->feature = feature;
+       helper->got_headers_handler = g_signal_connect (msg, "got-headers",
+                                                       G_CALLBACK (msg_got_headers_cb),
+                                                       helper);
+}
+
+static void
+attach (SoupSessionFeature *feature, SoupSession *session)
+{
+       WebKitSoupCache *cache = WEBKIT_SOUP_CACHE (feature);
+       cache->priv->session = session;
+
+       webkit_soup_cache_default_feature_interface->attach (feature, session);
+}
+
+static void
+webkit_soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+                                       gpointer interface_data)
+{
+       webkit_soup_cache_default_feature_interface =
+               g_type_default_interface_peek (SOUP_TYPE_SESSION_FEATURE);
+
+       feature_interface->attach = attach;
+       feature_interface->request_started = request_started;
+}
+
+static void
+webkit_soup_cache_init (WebKitSoupCache *cache)
+{
+       WebKitSoupCachePrivate *priv;
+
+       priv = cache->priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
+
+       priv->cache = g_hash_table_new_full (g_str_hash,
+                                            g_str_equal,
+                                            (GDestroyNotify)g_free,
+                                            NULL);
+
+       /* LRU */
+       priv->lru_start = NULL;
+
+       /* */
+       priv->n_pending = 0;
+
+       /* Cache size */
+       priv->max_size = DEFAULT_MAX_SIZE;
+       priv->max_entry_data_size = priv->max_size / MAX_ENTRY_DATA_PERCENTAGE;
+       priv->size = 0;
+}
+
+static void
+remove_cache_item (gpointer key,
+                  gpointer value,
+                  WebKitSoupCache *cache)
+{
+       WebKitSoupCacheEntry *entry = g_hash_table_lookup (cache->priv->cache, (const gchar *)key);
+       if (webkit_soup_cache_entry_remove (cache, entry))
+               webkit_soup_cache_entry_free (entry, FALSE);
+}
+
+static void
+webkit_soup_cache_finalize (GObject *object)
+{
+       WebKitSoupCachePrivate *priv;
+
+       priv = WEBKIT_SOUP_CACHE (object)->priv;
+
+       g_hash_table_foreach (priv->cache, (GHFunc)remove_cache_item, object);
+       g_hash_table_destroy (priv->cache);
+       g_free (priv->cache_dir);
+
+       g_list_free (priv->lru_start);
+       priv->lru_start = NULL;
+
+       G_OBJECT_CLASS (webkit_soup_cache_parent_class)->finalize (object);
+}
+
+static void
+webkit_soup_cache_set_property (GObject *object, guint prop_id,
+                               const GValue *value, GParamSpec *pspec)
+{
+       WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE (object)->priv;
+
+       switch (prop_id) {
+       case PROP_CACHE_DIR:
+               priv->cache_dir = g_value_dup_string (value);
+               /* Create directory if it does not exist (FIXME: should we?) */
+               if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
+                       g_mkdir_with_parents (priv->cache_dir, 0700);
+               break;
+       case PROP_CACHE_TYPE:
+               priv->cache_type = g_value_get_enum (value);
+               /* TODO: clear private entries and issue a warning if moving to shared? */
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+webkit_soup_cache_get_property (GObject *object, guint prop_id,
+                               GValue *value, GParamSpec *pspec)
+{
+       WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE (object)->priv;
+
+       switch (prop_id) {
+       case PROP_CACHE_DIR:
+               g_value_set_string (value, priv->cache_dir);
+               break;
+       case PROP_CACHE_TYPE:
+               g_value_set_enum (value, priv->cache_type);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+webkit_soup_cache_constructed (GObject *object)
+{
+       WebKitSoupCachePrivate *priv;
+
+       priv = WEBKIT_SOUP_CACHE (object)->priv;
+
+       if (!priv->cache_dir) {
+               /* Set a default cache dir, different for each user */
+               priv->cache_dir = g_build_filename (g_get_user_cache_dir (),
+                                                   "httpcache",
+                                                   NULL);
+               if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
+                       g_mkdir_with_parents (priv->cache_dir, 0700);
+       }
+
+       if (G_OBJECT_CLASS (webkit_soup_cache_parent_class)->constructed)
+               G_OBJECT_CLASS (webkit_soup_cache_parent_class)->constructed (object);
+}
+
+#define WEBKIT_SOUP_CACHE_TYPE_TYPE (webkit_soup_cache_type_get_type ())
+static GType
+webkit_soup_cache_type_get_type (void)
+{
+       static GType cache_type = 0;
+
+       static const GEnumValue cache_types[] = {
+               { WEBKIT_SOUP_CACHE_SINGLE_USER, "Single user cache", "user" },
+               { WEBKIT_SOUP_CACHE_SHARED, "Shared cache", "shared" },
+               { 0, NULL, NULL }
+       };
+
+       if (!cache_type) {
+               cache_type = g_enum_register_static ("WebKitSoupCacheType", cache_types);
+       }
+       return cache_type;
+}
+
+static void
+webkit_soup_cache_class_init (WebKitSoupCacheClass *cache_class)
+{
+       GObjectClass *gobject_class = (GObjectClass *)cache_class;
+
+       gobject_class->finalize = webkit_soup_cache_finalize;
+       gobject_class->constructed = webkit_soup_cache_constructed;
+       gobject_class->set_property = webkit_soup_cache_set_property;
+       gobject_class->get_property = webkit_soup_cache_get_property;
+
+       cache_class->get_cacheability = get_cacheability;
+
+       g_object_class_install_property (gobject_class, PROP_CACHE_DIR,
+                                        g_param_spec_string ("cache-dir",
+                                                             "Cache directory",
+                                                             "The directory to store the cache files",
+                                                             NULL,
+                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+       g_object_class_install_property (gobject_class, PROP_CACHE_TYPE,
+                                        g_param_spec_enum ("cache-type",
+                                                           "Cache type",
+                                                           "Whether the cache is private or shared",
+                                                           WEBKIT_SOUP_CACHE_TYPE_TYPE,
+                                                           WEBKIT_SOUP_CACHE_SINGLE_USER,
+                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+       g_type_class_add_private (cache_class, sizeof (WebKitSoupCachePrivate));
+}
+
+/**
+ * webkit_soup_cache_new:
+ * @cache_dir: the directory to store the cached data, or %NULL to use the default one
+ * @cache_type: the #WebKitSoupCacheType of the cache
+ *
+ * Creates a new #WebKitSoupCache.
+ *
+ * Returns: a new #WebKitSoupCache
+ *
+ * Since: 2.28
+ **/
+WebKitSoupCache *
+webkit_soup_cache_new (const char *cache_dir, WebKitSoupCacheType cache_type)
+{
+       return g_object_new (WEBKIT_TYPE_SOUP_CACHE,
+                            "cache-dir", cache_dir,
+                            "cache-type", cache_type,
+                            NULL);
+}
+
+/**
+ * webkit_soup_cache_has_response:
+ * @cache: a #WebKitSoupCache
+ * @msg: a #SoupMessage
+ *
+ * This function calculates whether the @cache object has a proper
+ * response for the request @msg given the flags both in the request
+ * and the cached reply and the time ellapsed since it was cached.
+ *
+ * Returns: whether or not the @cache has a valid response for @msg
+ **/
+WebKitSoupCacheResponse
+webkit_soup_cache_has_response (WebKitSoupCache *cache, SoupMessage *msg)
+{
+       char *key;
+       WebKitSoupCacheEntry *entry;
+       const char *cache_control;
+       GHashTable *hash;
+       gpointer value;
+       gboolean must_revalidate;
+       int max_age, max_stale, min_fresh;
+       GList *lru_item, *item;
+
+       key = soup_message_get_cache_key (msg);
+       entry = g_hash_table_lookup (cache->priv->cache, key);
+
+       /* 1. The presented Request-URI and that of stored response
+        * match
+        */
+       if (!entry)
+               return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
+
+       /* Increase hit count. Take sorting into account */
+       entry->hits++;
+       lru_item = g_list_find (cache->priv->lru_start, entry);
+       item = lru_item;
+       while (item->next && lru_compare_func (item->data, item->next->data) > 0)
+               item = g_list_next (item);
+
+       if (item != lru_item) {
+               cache->priv->lru_start = g_list_remove_link (cache->priv->lru_start, lru_item);
+               item = g_list_insert_sorted (item, lru_item->data, lru_compare_func);
+               g_list_free (lru_item);
+       }
+
+       if (entry->dirty || entry->being_validated)
+               return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
+
+       /* 2. The request method associated with the stored response
+        *  allows it to be used for the presented request
+        */
+
+       /* In practice this means we only return our resource for GET,
+        * cacheability for other methods is a TODO in the RFC
+        * (TODO: although we could return the headers for HEAD
+        * probably).
+        */
+       if (msg->method != SOUP_METHOD_GET)
+               return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
+
+       /* 3. Selecting request-headers nominated by the stored
+        * response (if any) match those presented.
+        */
+
+       /* TODO */
+
+       /* 4. The presented request and stored response are free from
+        * directives that would prevent its use.
+        */
+
+       must_revalidate = FALSE;
+       max_age = max_stale = min_fresh = -1;
+
+       cache_control = soup_message_headers_get (msg->request_headers, "Cache-Control");
+       if (cache_control) {
+               hash = soup_header_parse_param_list (cache_control);
+
+               if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) {
+                       g_hash_table_destroy (hash);
+                       return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
+               }
+
+               if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) {
+                       entry->must_revalidate = TRUE;
+               }
+
+               if (g_hash_table_lookup_extended (hash, "max-age", NULL, &value)) {
+                       max_age = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
+               }
+
+               /* max-stale can have no value set, we need to use _extended */
+               if (g_hash_table_lookup_extended (hash, "max-stale", NULL, &value)) {
+                       if (value)
+                               max_stale = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
+                       else
+                               max_stale = G_MAXINT32;
+               }
+
+               value = g_hash_table_lookup (hash, "min-fresh");
+               if (value)
+                       min_fresh = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
+
+               g_hash_table_destroy (hash);
+
+               if (max_age != -1) {
+                       guint current_age = webkit_soup_cache_entry_get_current_age (entry);
+
+                       /* If we are over max-age and max-stale is not
+                          set, do not use the value from the cache
+                          without validation */
+                       if (max_age <= current_age && max_stale == -1)
+                               return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
+               }
+       }
+
+       /* 5. The stored response is either: fresh, allowed to be
+        * served stale or succesfully validated
+        */
+       /* TODO consider also proxy-revalidate & s-maxage */
+       if (entry->must_revalidate)
+               return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
+
+       if (!webkit_soup_cache_entry_is_fresh_enough (entry, min_fresh)) {
+               /* Not fresh, can it be served stale? */
+               if (max_stale != -1) {
+                       /* G_MAXINT32 means we accept any staleness */
+                       if (max_stale == G_MAXINT32)
+                               return WEBKIT_SOUP_CACHE_RESPONSE_FRESH;
+
+                       if ((webkit_soup_cache_entry_get_current_age (entry) - entry->freshness_lifetime) <= max_stale)
+                               return WEBKIT_SOUP_CACHE_RESPONSE_FRESH;
+               }
+
+               return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
+       }
+
+       return WEBKIT_SOUP_CACHE_RESPONSE_FRESH;
+}
+
+/**
+ * webkit_soup_cache_get_cacheability:
+ * @cache: a #WebKitSoupCache
+ * @msg: a #SoupMessage
+ *
+ * Calculates whether the @msg can be cached or not.
+ *
+ * Returns: a #WebKitSoupCacheability value indicating whether the @msg can be cached or not.
+ **/
+WebKitSoupCacheability
+webkit_soup_cache_get_cacheability (WebKitSoupCache *cache, SoupMessage *msg)
+{
+       g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), WEBKIT_SOUP_CACHE_UNCACHEABLE);
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), WEBKIT_SOUP_CACHE_UNCACHEABLE);
+
+       return WEBKIT_SOUP_CACHE_GET_CLASS (cache)->get_cacheability (cache, msg);
+}
+
+static gboolean
+force_flush_timeout (gpointer data)
+{
+       gboolean *forced = (gboolean *)data;
+       *forced = TRUE;
+
+       return FALSE;
+}
+
+/**
+ * webkit_soup_cache_flush:
+ * @cache: a #WebKitSoupCache
+ * @session: the #SoupSession associated with the @cache
+ *
+ * This function will force all pending writes in the @cache to be
+ * committed to disk. For doing so it will iterate the #GMainContext
+ * associated with the @session (which can be the default one) as long
+ * as needed.
+ **/
+void
+webkit_soup_cache_flush (WebKitSoupCache *cache)
+{
+       GMainContext *async_context;
+       SoupSession *session;
+       guint timeout_id;
+       gboolean forced = FALSE;
+
+       g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache));
+
+       session = cache->priv->session;
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       async_context = soup_session_get_async_context (session);
+
+       /* We give cache 10 secs to finish */
+       timeout_id = g_timeout_add (10000, force_flush_timeout, &forced);
+
+       while (!forced && cache->priv->n_pending > 0)
+               g_main_context_iteration (async_context, FALSE);
+
+       if (!forced)
+               g_source_remove (timeout_id);
+       else
+               g_warning ("Cache flush finished despite %d pending requests", cache->priv->n_pending);
+}
+
+static void
+clear_cache_item (gpointer key,
+                 gpointer value,
+                 WebKitSoupCache *cache)
+{
+       WebKitSoupCacheEntry *entry = g_hash_table_lookup (cache->priv->cache, (const gchar *)key);
+       if (webkit_soup_cache_entry_remove (cache, entry))
+               webkit_soup_cache_entry_free (entry, TRUE);
+}
+
+/**
+ * webkit_soup_cache_clear:
+ * @cache: a #WebKitSoupCache
+ *
+ * Will remove all entries in the @cache plus all the cache files
+ * associated with them.
+ **/
+void
+webkit_soup_cache_clear (WebKitSoupCache *cache)
+{
+       GHashTable *hash;
+
+       g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache));
+
+       hash = cache->priv->cache;
+       g_return_if_fail (hash);
+
+       g_hash_table_foreach (hash, (GHFunc)clear_cache_item, cache);
+}
+
+SoupMessage *
+webkit_soup_cache_generate_conditional_request (WebKitSoupCache *cache, SoupMessage *original)
+{
+       SoupMessage *msg;
+       SoupURI *uri;
+       WebKitSoupCacheEntry *entry;
+       char *key;
+       const char *value;
+
+       g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), NULL);
+       g_return_val_if_fail (SOUP_IS_MESSAGE (original), NULL);
+
+       /* First copy the data we need from the original message */
+       uri = soup_message_get_uri (original);
+       msg = soup_message_new_from_uri (original->method, uri);
+
+       soup_message_headers_foreach (original->request_headers,
+                                     (SoupMessageHeadersForeachFunc)copy_headers,
+                                     msg->request_headers);
+
+       /* Now add the validator entries in the header from the cached
+          data */
+       key = soup_message_get_cache_key (original);
+       entry = g_hash_table_lookup (cache->priv->cache, key);
+       g_free (key);
+
+       g_return_val_if_fail (entry, NULL);
+
+       entry->being_validated = TRUE;
+
+       value = soup_message_headers_get (entry->headers, "Last-Modified");
+       if (value)
+               soup_message_headers_append (msg->request_headers,
+                                            "If-Modified-Since",
+                                            value);
+       value = soup_message_headers_get (entry->headers, "ETag");
+       if (value)
+               soup_message_headers_append (msg->request_headers,
+                                            "If-None-Match",
+                                            value);
+       return msg;
+}
+
+#define WEBKIT_SOUP_CACHE_FILE "soup.cache"
+
+#define WEBKIT_SOUP_CACHE_HEADERS_FORMAT "{ss}"
+#define WEBKIT_SOUP_CACHE_PHEADERS_FORMAT "(ssbuuuuua" WEBKIT_SOUP_CACHE_HEADERS_FORMAT ")"
+#define WEBKIT_SOUP_CACHE_ENTRIES_FORMAT "a" WEBKIT_SOUP_CACHE_PHEADERS_FORMAT
+
+/* Basically the same format than above except that some strings are
+   prepended with &. This way the GVariant returns a pointer to the
+   data instead of duplicating the string */
+#define WEBKIT_SOUP_CACHE_DECODE_HEADERS_FORMAT "{&s&s}"
+
+static void
+pack_entry (gpointer data,
+           gpointer user_data)
+{
+       WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *) data;
+       SoupMessageHeadersIter iter;
+       const gchar *header_key, *header_value;
+       GVariantBuilder *headers_builder;
+       GVariantBuilder *entries_builder = (GVariantBuilder *)user_data;
+
+       /* Do not store non-consolidated entries */
+       if (entry->dirty || entry->writing || !entry->key)
+               return;
+
+       /* Pack headers */
+       headers_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
+       soup_message_headers_iter_init (&iter, entry->headers);
+       while (soup_message_headers_iter_next (&iter, &header_key, &header_value)) {
+               if (g_utf8_validate (header_value, -1, NULL))
+                       g_variant_builder_add (headers_builder, WEBKIT_SOUP_CACHE_HEADERS_FORMAT,
+                                              header_key, header_value);
+       }
+
+       /* Entry data */
+       g_variant_builder_add (entries_builder, WEBKIT_SOUP_CACHE_PHEADERS_FORMAT,
+                              entry->key, entry->filename, entry->must_revalidate,
+                              entry->freshness_lifetime, entry->corrected_initial_age,
+                              entry->response_time, entry->hits, entry->length, headers_builder);
+
+       g_variant_builder_unref (headers_builder);
+}
+
+void
+webkit_soup_cache_dump (WebKitSoupCache *cache)
+{
+       WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
+       gchar *filename;
+       GVariantBuilder *entries_builder;
+       GVariant *cache_variant;
+
+       if (!g_list_length (cache->priv->lru_start))
+               return;
+
+       /* Create the builder and iterate over all entries */
+       entries_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
+       g_list_foreach (cache->priv->lru_start, pack_entry, entries_builder);
+
+       /* Serialize and dump */
+       cache_variant = g_variant_new (WEBKIT_SOUP_CACHE_ENTRIES_FORMAT, entries_builder);
+       g_variant_builder_unref (entries_builder);
+
+       filename = g_build_filename (priv->cache_dir, WEBKIT_SOUP_CACHE_FILE, NULL);
+       g_file_set_contents (filename, (const gchar *)g_variant_get_data (cache_variant),
+                            g_variant_get_size (cache_variant), NULL);
+       g_free (filename);
+       g_variant_unref (cache_variant);
+}
+
+void
+webkit_soup_cache_load (WebKitSoupCache *cache)
+{
+       gchar *filename = NULL, *contents = NULL;
+       GVariant *cache_variant;
+       GVariantIter *entries_iter, *headers_iter;
+       GVariantType *variant_format;
+       gsize length;
+       WebKitSoupCacheEntry *entry;
+       WebKitSoupCachePrivate *priv = cache->priv;
+
+       filename = g_build_filename (priv->cache_dir, WEBKIT_SOUP_CACHE_FILE, NULL);
+       if (!g_file_get_contents (filename, &contents, &length, NULL)) {
+               g_free (filename);
+               return;
+       }
+       g_free (filename);
+
+       variant_format = g_variant_type_new (WEBKIT_SOUP_CACHE_ENTRIES_FORMAT);
+       cache_variant = g_variant_new_from_data (variant_format, (const gchar *)contents, length, FALSE, NULL, NULL);
+       g_variant_type_free (variant_format);
+
+       g_variant_get (cache_variant, WEBKIT_SOUP_CACHE_ENTRIES_FORMAT, &entries_iter);
+       entry = g_slice_new0 (WebKitSoupCacheEntry);
+
+       while (g_variant_iter_loop (entries_iter, WEBKIT_SOUP_CACHE_PHEADERS_FORMAT,
+                                   &entry->key, &entry->filename, &entry->must_revalidate,
+                                   &entry->freshness_lifetime, &entry->corrected_initial_age,
+                                   &entry->response_time, &entry->hits, &entry->length,
+                                    &headers_iter)) {
+               const gchar *header_key, *header_value;
+
+               /* SoupMessage Headers */
+               entry->headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
+               while (g_variant_iter_loop (headers_iter, WEBKIT_SOUP_CACHE_DECODE_HEADERS_FORMAT, &header_key, &header_value))
+                       soup_message_headers_append (entry->headers, header_key, header_value);
+
+               /* Insert in cache */
+               if (!webkit_soup_cache_entry_insert_by_key (cache, (const gchar *)entry->key, entry, FALSE))
+                       webkit_soup_cache_entry_free (entry, TRUE);
+
+               /* New entry for the next iteration. This creates an
+                  extra object the last iteration but it's worth it
+                  as we save several if's */
+               entry = g_slice_new0 (WebKitSoupCacheEntry);
+       }
+       /* Remove last created entry */
+       g_slice_free (WebKitSoupCacheEntry, entry);
+
+       /* Sort LRU (shouldn't be needed). First reverse as elements
+        * are always prepended when inserting
+        */
+       cache->priv->lru_start = g_list_reverse (cache->priv->lru_start);
+       cache->priv->lru_start = g_list_sort (cache->priv->lru_start, lru_compare_func);
+
+       /* frees */
+       g_variant_iter_free (entries_iter);
+       g_variant_unref (cache_variant);
+}
+
+void
+webkit_soup_cache_set_max_size (WebKitSoupCache *cache,
+                               guint max_size)
+{
+       cache->priv->max_size = max_size;
+       cache->priv->max_entry_data_size = cache->priv->max_size / MAX_ENTRY_DATA_PERCENTAGE;
+}
+
+guint
+webkit_soup_cache_get_max_size (WebKitSoupCache *cache)
+{
+       return cache->priv->max_size;
+}
diff --git a/WebCore/platform/network/soup/cache/webkit/soup-cache.h b/WebCore/platform/network/soup/cache/webkit/soup-cache.h
new file mode 100644 (file)
index 0000000..9a62bb9
--- /dev/null
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cache.h:
+ *
+ * Copyright (C) 2009, 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WEBKIT_SOUP_CACHE_H
+#define WEBKIT_SOUP_CACHE_H 1
+
+#include <webkit/webkitdefines.h>
+
+#include <libsoup/soup-types.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_SOUP_CACHE            (webkit_soup_cache_get_type ())
+#define WEBKIT_SOUP_CACHE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCache))
+#define WEBKIT_SOUP_CACHE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCacheClass))
+#define WEBKIT_IS_SOUP_CACHE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_CACHE))
+#define WEBKIT_IS_SOUP_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), WEBKIT_TYPE_SOUP_CACHE))
+#define WEBKIT_SOUP_CACHE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCacheClass))
+
+typedef struct _WebKitSoupCache WebKitSoupCache;
+typedef struct _WebKitSoupCachePrivate WebKitSoupCachePrivate;
+
+typedef enum {
+       WEBKIT_SOUP_CACHE_CACHEABLE = (1 << 0),
+       WEBKIT_SOUP_CACHE_UNCACHEABLE = (1 << 1),
+       WEBKIT_SOUP_CACHE_INVALIDATES = (1 << 2),
+       WEBKIT_SOUP_CACHE_VALIDATES = (1 << 3)
+} WebKitSoupCacheability;
+
+typedef enum {
+       WEBKIT_SOUP_CACHE_RESPONSE_FRESH,
+       WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION,
+       WEBKIT_SOUP_CACHE_RESPONSE_STALE
+} WebKitSoupCacheResponse;
+
+typedef enum {
+       WEBKIT_SOUP_CACHE_SINGLE_USER,
+       WEBKIT_SOUP_CACHE_SHARED
+} WebKitSoupCacheType;
+
+struct _WebKitSoupCache {
+       GObject parent_instance;
+
+       WebKitSoupCachePrivate *priv;
+};
+
+typedef struct {
+       GObjectClass parent_class;
+
+       /* methods */
+       WebKitSoupCacheability (*get_cacheability)(WebKitSoupCache *cache, SoupMessage *msg);
+
+       /* Padding for future expansion */
+       void (*_libsoup_reserved1)(void);
+       void (*_libsoup_reserved2)(void);
+       void (*_libsoup_reserved3)(void);
+} WebKitSoupCacheClass;
+
+WEBKIT_API GType             webkit_soup_cache_get_type (void);
+WEBKIT_API WebKitSoupCache  *webkit_soup_cache_new (const char  *cache_dir,
+                                                   WebKitSoupCacheType cache_type);
+WEBKIT_API void              webkit_soup_cache_flush (WebKitSoupCache   *cache);
+WEBKIT_API void              webkit_soup_cache_clear (WebKitSoupCache   *cache);
+
+WEBKIT_API void              webkit_soup_cache_dump (WebKitSoupCache *cache);
+WEBKIT_API void              webkit_soup_cache_load (WebKitSoupCache *cache);
+
+WEBKIT_API void              webkit_soup_cache_set_max_size (WebKitSoupCache *cache,
+                                                            guint max_size);
+WEBKIT_API guint             webkit_soup_cache_get_max_size (WebKitSoupCache *cache);
+
+G_END_DECLS
+
+
+#endif /* WEBKIT_SOUP_CACHE_H */
+
index bfcc94e..7be99d4 100644 (file)
@@ -1,3 +1,14 @@
+2010-10-13  Sergio Villar Senin  <svillar@igalia.com>
+
+        Reviewed by Martin Robinson.
+
+        [GTK] Add HTTP caching support
+        https://bugs.webkit.org/show_bug.cgi?id=44261
+
+        Add include paths for the new soup HTTP cache code.
+
+        * GNUmakefile.am: Add paths for the new soup HTTP cache code.
+
 2010-10-13  Yi Shen  <yi.4.shen@nokia.com>
 
         Reviewed by Antonio Gomes.
index 1599e89..5257ece 100644 (file)
@@ -6,6 +6,7 @@ noinst_PROGRAMS += \
 # GtkLauncher
 Programs_GtkLauncher_CPPFLAGS = \
        -I$(srcdir)/WebKit/gtk \
+       -I$(srcdir)/WebCore/platform/network/soup/cache/ \
        -I$(top_builddir)/WebKit/gtk \
        -I$(top_builddir)/DerivedSources \
        $(global_cppflags) \
@@ -38,6 +39,7 @@ dumprendertree_cppflags := \
        -I$(srcdir)/WebKitTools/DumpRenderTree/gtk \
        -I$(srcdir)/WebKit/gtk \
        -I$(srcdir)/WebCore/platform/gtk \
+       -I$(srcdir)/WebCore/platform/network/soup/cache/ \
        -I$(top_builddir)/WebKit/gtk \
        -I$(top_builddir)/DerivedSources \
        $(global_cppflags) \
index 379fa8f..6b44f30 100644 (file)
@@ -108,7 +108,7 @@ AC_DEFUN_ONCE([_WEBKIT_CHECK_GLIB],
 [dnl
 dnl check for glib
 # Version requirements
-GLIB_REQUIRED_VERSION=2.21.3
+GLIB_REQUIRED_VERSION=2.24
 GOBJECT_REQUIRED_VERSION=2.0
 GTHREAD_REQUIRED_VERSION=2.0