| Differences between
and this patch
- a/ChangeLog +10 lines
Lines 1-3 a/ChangeLog_sec1
1
2010-09-06  Sergio Villar Senin  <svillar@igalia.com>
2
3
        Reviewed by NOBODY (OOPS!).
4
5
        [GTK] Add HTTP caching support
6
        https://bugs.webkit.org/show_bug.cgi?id=44261
7
8
        * GNUmakefile.am: added paths for soup HTTP cache code
9
        * autotools/webkit.m4: upgraded required glib version
10
1
2010-09-18  Kevin Ollivier  <kevino@theolliviers.com>
11
2010-09-18  Kevin Ollivier  <kevino@theolliviers.com>
2
12
3
        [wx] Build fix, fix use of wrong case in name.
13
        [wx] Build fix, fix use of wrong case in name.
- a/GNUmakefile.am -4 / +6 lines
Lines 299-326 global_cppflags += \ a/GNUmakefile.am_sec1
299
endif
299
endif
300
300
301
webkitgtk_static_h_api += \
301
webkitgtk_static_h_api += \
302
	$(srcdir)/WebCore/platform/network/soup/cache/webkit/soup-cache.h \
302
	$(srcdir)/WebKit/gtk/webkit/webkit.h \
303
	$(srcdir)/WebKit/gtk/webkit/webkit.h \
303
	$(srcdir)/WebKit/gtk/webkit/webkitdefines.h \
304
	$(srcdir)/WebKit/gtk/webkit/webkitdefines.h \
304
	$(srcdir)/WebKit/gtk/webkit/webkitdownload.h \
305
	$(srcdir)/WebKit/gtk/webkit/webkitdownload.h \
305
	$(srcdir)/WebKit/gtk/webkit/webkiterror.h \
306
	$(srcdir)/WebKit/gtk/webkit/webkiterror.h \
307
	$(srcdir)/WebKit/gtk/webkit/webkitgeolocationpolicydecision.h \
306
	$(srcdir)/WebKit/gtk/webkit/webkithittestresult.h \
308
	$(srcdir)/WebKit/gtk/webkit/webkithittestresult.h \
307
	$(srcdir)/WebKit/gtk/webkit/webkitnetworkrequest.h \
309
	$(srcdir)/WebKit/gtk/webkit/webkitnetworkrequest.h \
308
	$(srcdir)/WebKit/gtk/webkit/webkitnetworkresponse.h \
310
	$(srcdir)/WebKit/gtk/webkit/webkitnetworkresponse.h \
311
	$(srcdir)/WebKit/gtk/webkit/webkitsecurityorigin.h \
309
	$(srcdir)/WebKit/gtk/webkit/webkitsoupauthdialog.h \
312
	$(srcdir)/WebKit/gtk/webkit/webkitsoupauthdialog.h \
310
	$(srcdir)/WebKit/gtk/webkit/webkitwebbackforwardlist.h \
313
	$(srcdir)/WebKit/gtk/webkit/webkitwebbackforwardlist.h \
314
	$(srcdir)/WebKit/gtk/webkit/webkitwebdatabase.h \
311
	$(srcdir)/WebKit/gtk/webkit/webkitwebdatasource.h \
315
	$(srcdir)/WebKit/gtk/webkit/webkitwebdatasource.h \
312
	$(srcdir)/WebKit/gtk/webkit/webkitwebframe.h \
316
	$(srcdir)/WebKit/gtk/webkit/webkitwebframe.h \
313
	$(srcdir)/WebKit/gtk/webkit/webkitwebhistoryitem.h \
317
	$(srcdir)/WebKit/gtk/webkit/webkitwebhistoryitem.h \
314
	$(srcdir)/WebKit/gtk/webkit/webkitwebinspector.h \
318
	$(srcdir)/WebKit/gtk/webkit/webkitwebinspector.h \
315
	$(srcdir)/WebKit/gtk/webkit/webkitwebnavigationaction.h \
319
	$(srcdir)/WebKit/gtk/webkit/webkitwebnavigationaction.h \
316
	$(srcdir)/WebKit/gtk/webkit/webkitwebpolicydecision.h \
320
	$(srcdir)/WebKit/gtk/webkit/webkitwebpolicydecision.h \
317
	$(srcdir)/WebKit/gtk/webkit/webkitgeolocationpolicydecision.h \
318
	$(srcdir)/WebKit/gtk/webkit/webkitwebresource.h \
321
	$(srcdir)/WebKit/gtk/webkit/webkitwebresource.h \
319
	$(srcdir)/WebKit/gtk/webkit/webkitwebsettings.h \
322
	$(srcdir)/WebKit/gtk/webkit/webkitwebsettings.h \
320
	$(srcdir)/WebKit/gtk/webkit/webkitwebwindowfeatures.h \
323
	$(srcdir)/WebKit/gtk/webkit/webkitwebwindowfeatures.h \
321
	$(srcdir)/WebKit/gtk/webkit/webkitwebview.h \
324
	$(srcdir)/WebKit/gtk/webkit/webkitwebview.h
322
	$(srcdir)/WebKit/gtk/webkit/webkitwebdatabase.h \
323
	$(srcdir)/WebKit/gtk/webkit/webkitsecurityorigin.h
324
325
325
webkitgtk_built_h_api += \
326
webkitgtk_built_h_api += \
326
	WebKit/gtk/webkit/webkitversion.h
327
	WebKit/gtk/webkit/webkitversion.h
Lines 541-546 noinst_PROGRAMS += $(TEST_PROGS) a/GNUmakefile.am_sec2
541
webkit_tests_cflags = \
542
webkit_tests_cflags = \
542
	-fno-strict-aliasing \
543
	-fno-strict-aliasing \
543
	-I$(srcdir)/JavaScriptCore/ForwardingHeaders \
544
	-I$(srcdir)/JavaScriptCore/ForwardingHeaders \
545
	-I$(srcdir)/WebCore/platform/network/soup/cache \
544
	-I$(srcdir)/WebKit/gtk \
546
	-I$(srcdir)/WebKit/gtk \
545
	-I$(top_builddir)/WebKit/gtk \
547
	-I$(top_builddir)/WebKit/gtk \
546
	-I$(top_builddir)/DerivedSources \
548
	-I$(top_builddir)/DerivedSources \
- a/WebCore/ChangeLog +193 lines
Lines 1-3 a/WebCore/ChangeLog_sec1
1
2010-09-06  Sergio Villar Senin  <svillar@igalia.com>
2
3
        Reviewed by NOBODY (OOPS!).
4
5
        [GTK] Add HTTP caching support
6
        https://bugs.webkit.org/show_bug.cgi?id=44261
7
8
        * GNUmakefile.am:
9
        * platform/network/ResourceHandleInternal.h:
10
        (WebCore::ResourceHandleInternal::ResourceHandleInternal):
11
        * platform/network/soup/ResourceHandleSoup.cpp:
12
        (WebCore::ResourceHandleInternal::~ResourceHandleInternal):
13
        (WebCore::ResourceHandle::~ResourceHandle):
14
        (WebCore::restartedCallback):
15
        (WebCore::gotChunkCallback):
16
        (WebCore::parseDataUrl):
17
        (WebCore::cleanupSoupRequestOperation):
18
        (WebCore::sendRequestCallback):
19
        (WebCore::startHttp):
20
        * platform/network/soup/ResourceRequest.h:
21
        * platform/network/soup/ResourceRequestSoup.cpp:
22
        (WebCore::ResourceRequest::updateSoupMessage):
23
        * platform/network/soup/cache/soup-directory-input-stream.c: Added.
24
        (soup_directory_input_stream_parse_info):
25
        (soup_directory_input_stream_read_next_file):
26
        (soup_directory_input_stream_read):
27
        (soup_directory_input_stream_close):
28
        (soup_directory_input_stream_class_init):
29
        (soup_directory_input_stream_init):
30
        (soup_directory_input_stream_new):
31
        * platform/network/soup/cache/soup-directory-input-stream.h: Added.
32
        * platform/network/soup/cache/soup-http-input-stream.c: Added.
33
        (soup_http_input_stream_finalize):
34
        (soup_http_input_stream_class_init):
35
        (soup_http_input_stream_seekable_iface_init):
36
        (soup_http_input_stream_init):
37
        (soup_http_input_stream_queue_message):
38
        (soup_http_input_stream_new):
39
        (soup_http_input_stream_got_headers):
40
        (soup_http_input_stream_got_chunk):
41
        (soup_http_input_stream_finished):
42
        (soup_http_input_stream_cancelled):
43
        (soup_http_input_stream_prepare_for_io):
44
        (soup_http_input_stream_done_io):
45
        (set_error_if_http_failed):
46
        (read_from_leftover):
47
        (soup_http_input_stream_send_internal):
48
        (send_sync_finished):
49
        (soup_http_input_stream_send):
50
        (soup_http_input_stream_read):
51
        (soup_http_input_stream_close):
52
        (wrapper_callback):
53
        (send_async_thread):
54
        (soup_http_input_stream_send_async_in_thread):
55
        (send_async_finished):
56
        (soup_http_input_stream_send_async_internal):
57
        (soup_http_input_stream_send_async):
58
        (soup_http_input_stream_send_finish):
59
        (read_async_done):
60
        (soup_http_input_stream_read_async):
61
        (soup_http_input_stream_read_finish):
62
        (soup_http_input_stream_close_async):
63
        (soup_http_input_stream_close_finish):
64
        (soup_http_input_stream_tell):
65
        (soup_http_input_stream_can_seek):
66
        (soup_http_input_stream_seek):
67
        (soup_http_input_stream_can_truncate):
68
        (soup_http_input_stream_truncate):
69
        (soup_http_input_stream_get_message):
70
        * platform/network/soup/cache/soup-http-input-stream.h: Added.
71
        * platform/network/soup/cache/soup-request-data.c: Added.
72
        (soup_request_data_init):
73
        (soup_request_data_finalize):
74
        (soup_request_data_check_uri):
75
        (uri_decoded_copy):
76
        (soup_request_data_send):
77
        (soup_request_data_get_content_length):
78
        (soup_request_data_get_content_type):
79
        (soup_request_data_class_init):
80
        * platform/network/soup/cache/soup-request-data.h: Added.
81
        * platform/network/soup/cache/soup-request-file.c: Added.
82
        (soup_request_file_get_file):
83
        (soup_request_file_init):
84
        (soup_request_file_finalize):
85
        (soup_request_file_check_uri):
86
        (soup_request_file_ftp_main_loop_quit):
87
        (soup_request_file_ensure_file_ftp):
88
        (soup_request_file_ensure_file):
89
        (soup_request_file_send):
90
        (soup_request_file_send_async_thread):
91
        (soup_request_file_send_async):
92
        (soup_request_file_send_finish):
93
        (soup_request_file_get_content_length):
94
        (soup_request_file_get_content_type):
95
        (soup_request_file_class_init):
96
        * platform/network/soup/cache/soup-request-file.h: Added.
97
        * platform/network/soup/cache/soup-request-http.c: Added.
98
        (soup_request_http_get_message):
99
        (soup_request_http_init):
100
        (soup_request_http_check_uri):
101
        (soup_request_http_finalize):
102
        (soup_request_http_send):
103
        (sent_async):
104
        (conditional_get_ready_cb):
105
        (send_async_cb):
106
        (soup_request_http_send_async):
107
        (soup_request_http_send_finish):
108
        (soup_request_http_get_content_length):
109
        (soup_request_http_get_content_type):
110
        (soup_request_http_class_init):
111
        * platform/network/soup/cache/soup-request-http.h: Added.
112
        * platform/network/soup/cache/soup-request.c: Added.
113
        (soup_request_init):
114
        (soup_request_finalize):
115
        (soup_request_set_property):
116
        (soup_request_get_property):
117
        (soup_request_initable_init):
118
        (soup_request_default_check_uri):
119
        (soup_request_default_send_async):
120
        (soup_request_default_send_finish):
121
        (soup_request_send):
122
        (soup_request_send_async):
123
        (soup_request_send_finish):
124
        (soup_request_class_init):
125
        (soup_request_initable_interface_init):
126
        (soup_request_get_uri):
127
        (soup_request_get_session):
128
        (soup_request_get_content_length):
129
        (soup_request_get_content_type):
130
        * platform/network/soup/cache/soup-request.h: Added.
131
        * platform/network/soup/cache/soup-requester.c: Added.
132
        (soup_requester_init):
133
        (finalize):
134
        (soup_requester_class_init):
135
        (init_request_types):
136
        (soup_requester_new):
137
        (soup_requester_request):
138
        (soup_requester_request_uri):
139
        (soup_scheme_is_valid):
140
        (soup_requester_add_protocol):
141
        (soup_requester_remove_protocol):
142
        (soup_error_quark):
143
        * platform/network/soup/cache/soup-requester.h: Added.
144
        * platform/network/soup/cache/webkit/soup-cache-private.h: Added.
145
        * platform/network/soup/cache/webkit/soup-cache.c: Added.
146
        (get_cacheability):
147
        (webkit_soup_cache_entry_free):
148
        (copy_headers):
149
        (update_headers):
150
        (webkit_soup_cache_entry_get_current_age):
151
        (webkit_soup_cache_entry_is_fresh_enough):
152
        (soup_message_get_cache_key):
153
        (webkit_soup_cache_entry_set_freshness):
154
        (webkit_soup_cache_entry_new):
155
        (webkit_soup_cache_writing_fixture_free):
156
        (close_ready_cb):
157
        (write_ready_cb):
158
        (msg_got_chunk_cb):
159
        (msg_got_body_cb):
160
        (webkit_soup_cache_entry_delete):
161
        (lru_compare_func):
162
        (cache_accepts_entries_of_size):
163
        (make_room_for_new_entry):
164
        (webkit_soup_cache_entry_insert_by_key):
165
        (msg_restarted_cb):
166
        (append_to_ready_cb):
167
        (msg_got_headers_cb):
168
        (webkit_soup_cache_send_response):
169
        (request_started):
170
        (attach):
171
        (webkit_soup_cache_session_feature_init):
172
        (webkit_soup_cache_init):
173
        (webkit_soup_cache_finalize):
174
        (webkit_soup_cache_set_property):
175
        (webkit_soup_cache_get_property):
176
        (webkit_soup_cache_constructed):
177
        (webkit_soup_cache_type_get_type):
178
        (webkit_soup_cache_class_init):
179
        (webkit_soup_cache_new):
180
        (webkit_soup_cache_has_response):
181
        (webkit_soup_cache_get_cacheability):
182
        (force_flush_timeout):
183
        (webkit_soup_cache_flush):
184
        (remove_cache_item):
185
        (webkit_soup_cache_clear):
186
        (webkit_soup_cache_generate_conditional_request):
187
        (pack_entry):
188
        (webkit_soup_cache_dump):
189
        (webkit_soup_cache_load):
190
        (webkit_soup_cache_set_max_size):
191
        (webkit_soup_cache_get_max_size):
192
        * platform/network/soup/cache/webkit/soup-cache.h: Added.
193
1
2010-09-19  Sheriff Bot  <webkit.review.bot@gmail.com>
194
2010-09-19  Sheriff Bot  <webkit.review.bot@gmail.com>
2
195
3
        Unreviewed, rolling out r67749.
196
        Unreviewed, rolling out r67749.
- a/WebCore/GNUmakefile.am -1 / +19 lines
Lines 79-85 webcoregtk_cppflags += \ a/WebCore/GNUmakefile.am_sec1
79
	-I$(srcdir)/WebCore/platform/graphics/gstreamer \
79
	-I$(srcdir)/WebCore/platform/graphics/gstreamer \
80
	-I$(srcdir)/WebCore/platform/graphics/gtk \
80
	-I$(srcdir)/WebCore/platform/graphics/gtk \
81
	-I$(srcdir)/WebCore/platform/gtk \
81
	-I$(srcdir)/WebCore/platform/gtk \
82
	-I$(srcdir)/WebCore/platform/network/soup
82
	-I$(srcdir)/WebCore/platform/network/soup \
83
	-I$(srcdir)/WebCore/platform/network/soup/cache \
84
	-I$(srcdir)/WebCore/platform/network/soup/cache/webkit
83
85
84
webcore_built_nosources += \
86
webcore_built_nosources += \
85
	DerivedSources/WebCore/DocTypeStrings.cpp \
87
	DerivedSources/WebCore/DocTypeStrings.cpp \
Lines 3473-3478 webcoregtk_sources += \ a/WebCore/GNUmakefile.am_sec2
3473
	WebCore/platform/network/soup/SocketStreamError.h \
3475
	WebCore/platform/network/soup/SocketStreamError.h \
3474
	WebCore/platform/network/soup/SocketStreamHandle.h \
3476
	WebCore/platform/network/soup/SocketStreamHandle.h \
3475
	WebCore/platform/network/soup/SocketStreamHandleSoup.cpp \
3477
	WebCore/platform/network/soup/SocketStreamHandleSoup.cpp \
3478
	WebCore/platform/network/soup/cache/soup-directory-input-stream.c \
3479
	WebCore/platform/network/soup/cache/soup-directory-input-stream.h \
3480
	WebCore/platform/network/soup/cache/soup-http-input-stream.c \
3481
	WebCore/platform/network/soup/cache/soup-http-input-stream.h \
3482
	WebCore/platform/network/soup/cache/soup-request-data.c \
3483
	WebCore/platform/network/soup/cache/soup-request-data.h \
3484
	WebCore/platform/network/soup/cache/soup-request-file.c \
3485
	WebCore/platform/network/soup/cache/soup-request-file.h \
3486
	WebCore/platform/network/soup/cache/soup-request-http.c \
3487
	WebCore/platform/network/soup/cache/soup-request-http.h \
3488
	WebCore/platform/network/soup/cache/soup-request.c \
3489
	WebCore/platform/network/soup/cache/soup-request.h \
3490
	WebCore/platform/network/soup/cache/soup-requester.c \
3491
	WebCore/platform/network/soup/cache/soup-requester.h \
3492
	WebCore/platform/network/soup/cache/webkit/soup-cache.c \
3493
	WebCore/platform/network/soup/cache/webkit/soup-cache.h \
3476
	WebCore/plugins/gtk/PluginDataGtk.cpp \
3494
	WebCore/plugins/gtk/PluginDataGtk.cpp \
3477
	WebCore/plugins/gtk/PluginPackageGtk.cpp \
3495
	WebCore/plugins/gtk/PluginPackageGtk.cpp \
3478
	WebCore/plugins/gtk/PluginViewGtk.cpp
3496
	WebCore/plugins/gtk/PluginViewGtk.cpp
- a/WebCore/platform/network/soup/cache/soup-directory-input-stream.c +199 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-directory-input-stream.c_sec1
1
/*
2
 * Copyright (C) 2008 Red Hat, Inc.
3
 * Copyright (C) 2010 Igalia, S.L.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Library General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Library General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Library General Public License
16
 * along with this library; see the file COPYING.LIB.  If not, write to
17
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
 * Boston, MA 02110-1301, USA.
19
 */
20
21
#ifdef HAVE_CONFIG_H
22
#include <config.h>
23
#endif
24
25
#include "soup-directory-input-stream.h"
26
27
#include <libsoup/soup.h>
28
#include <stdio.h>
29
#include <string.h>
30
31
#define INIT_STRING "<html><head><title>OMG!</title></head><body><table>"
32
#define EXIT_STRING "</table></html>"
33
34
G_DEFINE_TYPE (SoupDirectoryInputStream, soup_directory_input_stream, G_TYPE_INPUT_STREAM)
35
36
static SoupBuffer *
37
soup_directory_input_stream_parse_info (SoupDirectoryInputStream * stream,
38
					GFileInfo * info)
39
{
40
	SoupBuffer *buffer;
41
	GString *string;
42
	const char *s;
43
	char *escaped, *path, *xml_string;
44
45
	if (!g_file_info_get_name (info))
46
		return NULL;
47
48
	s = g_file_info_get_display_name (info);
49
	if (!s) {
50
		s = g_file_info_get_name (info);
51
		/* FIXME: convert somehow? */
52
		if (!g_utf8_validate (s, -1, NULL))
53
			return NULL;
54
	}
55
	string = g_string_new ("<tr>");
56
57
	xml_string = g_markup_escape_text (s, -1);
58
	escaped = g_uri_escape_string (g_file_info_get_name (info), NULL, FALSE);
59
	path = g_strconcat (stream->uri, "/", escaped, NULL);
60
	g_free (escaped);
61
	g_string_append_printf (string, "<td><a href=\"%s\">%s</a></td>", path, xml_string);
62
	g_free (path);
63
	g_free (xml_string);
64
	g_string_append (string, "</tr>");
65
66
	buffer = soup_buffer_new (SOUP_MEMORY_TAKE, string->str, string->len);
67
	g_string_free (string, FALSE);
68
69
	return buffer;
70
}
71
72
static SoupBuffer *
73
soup_directory_input_stream_read_next_file (SoupDirectoryInputStream  *stream,
74
					    GCancellable              *cancellable,
75
					    GError                   **error)
76
{
77
	GFileInfo *info;
78
	SoupBuffer *buffer;
79
	GError *err = NULL;
80
81
	do {
82
		info = g_file_enumerator_next_file (stream->enumerator, cancellable, &err);
83
		if (info == NULL) {
84
			if (err) {
85
				g_propagate_error (error, err);
86
				return NULL;
87
			} else if (!stream->done) {
88
				stream->done = TRUE;
89
				return soup_buffer_new (SOUP_MEMORY_STATIC,
90
							EXIT_STRING,
91
							sizeof (EXIT_STRING));
92
			} else {
93
				return NULL;
94
			}
95
		}
96
97
		buffer = soup_directory_input_stream_parse_info (stream, info);
98
	} while (buffer == NULL);
99
100
	return buffer;
101
}
102
103
static gssize
104
soup_directory_input_stream_read (GInputStream  *input,
105
				  void          *buffer,
106
				  gsize count,
107
				  GCancellable  *cancellable,
108
				  GError       **error)
109
{
110
	SoupDirectoryInputStream *stream = SOUP_DIRECTORY_INPUT_STREAM (input);
111
	gssize total, size;
112
113
	for (total = 0; total < count; total += size) {
114
		if (stream->buffer == NULL) {
115
			stream->buffer = soup_directory_input_stream_read_next_file (stream, cancellable, error);
116
			if (stream->buffer == NULL) {
117
				/* FIXME: Is this correct or should we forward the error? */
118
				if (total)
119
					g_clear_error (error);
120
				return total;
121
			}
122
		}
123
124
		size = MIN (stream->buffer->length, count - total);
125
		memcpy ((char *)buffer + total, stream->buffer->data, size);
126
		if (size == stream->buffer->length) {
127
			soup_buffer_free (stream->buffer);
128
			stream->buffer = NULL;
129
		} else {
130
			SoupBuffer *sub = soup_buffer_new_subbuffer (stream->buffer,
131
								     size,
132
								     stream->buffer->length - size);
133
			soup_buffer_free (stream->buffer);
134
			stream->buffer = sub;
135
		}
136
	}
137
138
	return total;
139
}
140
141
static gboolean
142
soup_directory_input_stream_close (GInputStream  *input,
143
				   GCancellable  *cancellable,
144
				   GError       **error)
145
{
146
	SoupDirectoryInputStream *stream = SOUP_DIRECTORY_INPUT_STREAM (input);
147
	gboolean result;
148
149
	if (stream->buffer) {
150
		soup_buffer_free (stream->buffer);
151
		stream->buffer = NULL;
152
	}
153
154
	result = g_file_enumerator_close (stream->enumerator,
155
					  cancellable,
156
					  error);
157
	g_object_unref (stream->enumerator);
158
	stream->enumerator = NULL;
159
160
	g_free (stream->uri);
161
	stream->uri = NULL;
162
163
	return result;
164
}
165
166
static void
167
soup_directory_input_stream_class_init (SoupDirectoryInputStreamClass *stream_class)
168
{
169
	GInputStreamClass *inputstream_class = G_INPUT_STREAM_CLASS (stream_class);
170
171
	inputstream_class->read_fn = soup_directory_input_stream_read;
172
	inputstream_class->close_fn = soup_directory_input_stream_close;
173
}
174
175
static void
176
soup_directory_input_stream_init (SoupDirectoryInputStream *stream)
177
{
178
	stream->buffer = soup_buffer_new (SOUP_MEMORY_STATIC,
179
					  INIT_STRING,
180
					  sizeof (INIT_STRING));
181
}
182
183
GInputStream *
184
soup_directory_input_stream_new (GFileEnumerator *enumerator,
185
				 SoupURI         *uri)
186
{
187
	GInputStream *stream;
188
189
	g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL);
190
	g_return_val_if_fail (uri != NULL, NULL);
191
192
	stream = g_object_new (SOUP_TYPE_DIRECTORY_INPUT_STREAM, NULL);
193
194
	SOUP_DIRECTORY_INPUT_STREAM (stream)->enumerator = g_object_ref (enumerator);
195
	SOUP_DIRECTORY_INPUT_STREAM (stream)->uri = soup_uri_to_string (uri, FALSE);
196
197
	return stream;
198
}
199
- a/WebCore/platform/network/soup/cache/soup-directory-input-stream.h +61 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-directory-input-stream.h_sec1
1
/*
2
 * Copyright (C) 2010 Red Hat, Inc.
3
 * Copyright (C) 2010 Igalia, S.L.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Library General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Library General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Library General Public License
16
 * along with this library; see the file COPYING.LIB.  If not, write to
17
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
 * Boston, MA 02110-1301, USA.
19
 */
20
21
#ifndef SOUP_DIRECTORY_INPUT_STREAM_H
22
#define SOUP_DIRECTORY_INPUT_STREAM_H 1
23
24
#include <gio/gio.h>
25
#include <libsoup/soup-types.h>
26
#include <libsoup/soup-message-body.h>
27
28
G_BEGIN_DECLS
29
30
#define SOUP_TYPE_DIRECTORY_INPUT_STREAM            (soup_directory_input_stream_get_type ())
31
#define SOUP_DIRECTORY_INPUT_STREAM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_DIRECTORY_INPUT_STREAM, SoupDirectoryInputStream))
32
#define SOUP_DIRECTORY_INPUT_STREAM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_DIRECTORY_INPUT_STREAM, SoupDirectoryInputStreamClass))
33
#define SOUP_IS_DIRECTORY_INPUT_STREAM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_DIRECTORY_INPUT_STREAM))
34
#define SOUP_IS_DIRECTORY_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_DIRECTORY_INPUT_STREAM))
35
#define SOUP_DIRECTORY_INPUT_STREAM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_DIRECTORY_INPUT_STREAM, SoupDirectoryInputStreamClass))
36
37
typedef struct _SoupDirectoryInputStream SoupDirectoryInputStream;
38
typedef struct _SoupDirectoryInputStreamClass SoupDirectoryInputStreamClass;
39
40
struct _SoupDirectoryInputStream {
41
	GInputStream parent;
42
43
	GFileEnumerator *enumerator;
44
	char *uri;
45
	SoupBuffer *buffer;
46
	gboolean done;
47
};
48
49
struct _SoupDirectoryInputStreamClass {
50
	GInputStreamClass parent_class;
51
};
52
53
GType          soup_directory_input_stream_get_type (void);
54
55
GInputStream *soup_directory_input_stream_new (GFileEnumerator *enumerator,
56
					       SoupURI         *uri);
57
58
59
G_END_DECLS
60
61
#endif /* SOUP_DIRECTORY_INPUT_STREAM_H */
- a/WebCore/platform/network/soup/cache/soup-http-input-stream.c +921 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-http-input-stream.c_sec1
1
/* soup-input-stream.c, based on gsocketinputstream.c
2
 *
3
 * Copyright (C) 2006-2007 Red Hat, Inc.
4
 * Copyright (C) 2010 Igalia, S.L.
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General
17
 * Public License along with this library; if not, write to the
18
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19
 * Boston, MA 02111-1307, USA.
20
 */
21
22
#include <config.h>
23
24
#include <string.h>
25
26
#include <glib.h>
27
#include <gio/gio.h>
28
29
#include <libsoup/soup.h>
30
31
#include "soup-http-input-stream.h"
32
33
static void soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface);
34
35
G_DEFINE_TYPE_WITH_CODE (SoupHTTPInputStream, soup_http_input_stream, G_TYPE_INPUT_STREAM,
36
			 G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
37
						soup_http_input_stream_seekable_iface_init))
38
39
typedef void (*SoupHTTPInputStreamCallback)(GInputStream *);
40
41
typedef struct {
42
	SoupSession *session;
43
	GMainContext *async_context;
44
	SoupMessage *msg;
45
	gboolean got_headers, finished;
46
	goffset offset;
47
48
	GCancellable *cancellable;
49
	GSource *cancel_watch;
50
	SoupHTTPInputStreamCallback got_headers_cb;
51
	SoupHTTPInputStreamCallback got_chunk_cb;
52
	SoupHTTPInputStreamCallback finished_cb;
53
	SoupHTTPInputStreamCallback cancelled_cb;
54
55
	guchar *leftover_buffer;
56
	gsize leftover_bufsize, leftover_offset;
57
58
	guchar *caller_buffer;
59
	gsize caller_bufsize, caller_nread;
60
	GAsyncReadyCallback outstanding_callback;
61
	GSimpleAsyncResult *result;
62
} SoupHTTPInputStreamPrivate;
63
#define SOUP_HTTP_INPUT_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_HTTP_INPUT_STREAM, SoupHTTPInputStreamPrivate))
64
65
66
static gssize   soup_http_input_stream_read (GInputStream         *stream,
67
					     void                 *buffer,
68
					     gsize count,
69
					     GCancellable         *cancellable,
70
					     GError              **error);
71
static gboolean soup_http_input_stream_close (GInputStream         *stream,
72
					      GCancellable         *cancellable,
73
					      GError              **error);
74
static void     soup_http_input_stream_read_async (GInputStream         *stream,
75
						   void                 *buffer,
76
						   gsize count,
77
						   int io_priority,
78
						   GCancellable         *cancellable,
79
						   GAsyncReadyCallback callback,
80
						   gpointer data);
81
static gssize   soup_http_input_stream_read_finish (GInputStream         *stream,
82
						    GAsyncResult         *result,
83
						    GError              **error);
84
static void     soup_http_input_stream_close_async (GInputStream         *stream,
85
						    int io_priority,
86
						    GCancellable         *cancellable,
87
						    GAsyncReadyCallback callback,
88
						    gpointer data);
89
static gboolean soup_http_input_stream_close_finish (GInputStream         *stream,
90
						     GAsyncResult         *result,
91
						     GError              **error);
92
93
static goffset  soup_http_input_stream_tell (GSeekable            *seekable);
94
95
static gboolean soup_http_input_stream_can_seek (GSeekable            *seekable);
96
static gboolean soup_http_input_stream_seek (GSeekable            *seekable,
97
					     goffset offset,
98
					     GSeekType type,
99
					     GCancellable         *cancellable,
100
					     GError              **error);
101
102
static gboolean soup_http_input_stream_can_truncate (GSeekable            *seekable);
103
static gboolean soup_http_input_stream_truncate (GSeekable            *seekable,
104
						 goffset offset,
105
						 GCancellable         *cancellable,
106
						 GError              **error);
107
108
static void soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream);
109
static void soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer stream);
110
static void soup_http_input_stream_finished (SoupMessage *msg, gpointer stream);
111
112
static void
113
soup_http_input_stream_finalize (GObject *object)
114
{
115
	SoupHTTPInputStream *stream = SOUP_HTTP_INPUT_STREAM (object);
116
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
117
118
	g_object_unref (priv->session);
119
120
	g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (soup_http_input_stream_got_headers), stream);
121
	g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (soup_http_input_stream_got_chunk), stream);
122
	g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (soup_http_input_stream_finished), stream);
123
	g_object_unref (priv->msg);
124
	g_free (priv->leftover_buffer);
125
126
	if (G_OBJECT_CLASS (soup_http_input_stream_parent_class)->finalize)
127
		(*G_OBJECT_CLASS (soup_http_input_stream_parent_class)->finalize)(object);
128
}
129
130
static void
131
soup_http_input_stream_class_init (SoupHTTPInputStreamClass *klass)
132
{
133
	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
134
	GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
135
136
	g_type_class_add_private (klass, sizeof (SoupHTTPInputStreamPrivate));
137
138
	gobject_class->finalize = soup_http_input_stream_finalize;
139
140
	stream_class->read_fn = soup_http_input_stream_read;
141
	stream_class->close_fn = soup_http_input_stream_close;
142
	stream_class->read_async = soup_http_input_stream_read_async;
143
	stream_class->read_finish = soup_http_input_stream_read_finish;
144
	stream_class->close_async = soup_http_input_stream_close_async;
145
	stream_class->close_finish = soup_http_input_stream_close_finish;
146
}
147
148
static void
149
soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface)
150
{
151
	seekable_iface->tell = soup_http_input_stream_tell;
152
	seekable_iface->can_seek = soup_http_input_stream_can_seek;
153
	seekable_iface->seek = soup_http_input_stream_seek;
154
	seekable_iface->can_truncate = soup_http_input_stream_can_truncate;
155
	seekable_iface->truncate_fn = soup_http_input_stream_truncate;
156
}
157
158
static void
159
soup_http_input_stream_init (SoupHTTPInputStream *stream)
160
{
161
	;
162
}
163
164
static void
165
soup_http_input_stream_queue_message (SoupHTTPInputStream *stream)
166
{
167
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
168
169
	priv->got_headers = priv->finished = FALSE;
170
171
	/* Add an extra ref since soup_session_queue_message steals one */
172
	g_object_ref (priv->msg);
173
	soup_session_queue_message (priv->session, priv->msg, NULL, NULL);
174
}
175
176
/**
177
 * soup_http_input_stream_new:
178
 * @session: the #SoupSession to use
179
 * @msg: the #SoupMessage whose response will be streamed
180
 *
181
 * Prepares to send @msg over @session, and returns a #GInputStream
182
 * that can be used to read the response.
183
 *
184
 * @msg may not be sent until the first read call; if you need to look
185
 * at the status code or response headers before reading the body, you
186
 * can use soup_http_input_stream_send() or soup_http_input_stream_send_async()
187
 * to force the message to be sent and the response headers read.
188
 *
189
 * If @msg gets a non-2xx result, the first read (or send) will return
190
 * an error with type %SOUP_HTTP_INPUT_STREAM_HTTP_ERROR.
191
 *
192
 * Internally, #SoupHTTPInputStream is implemented using asynchronous I/O,
193
 * so if you are using the synchronous API (eg,
194
 * g_input_stream_read()), you should create a new #GMainContext and
195
 * set it as the %SOUP_SESSION_ASYNC_CONTEXT property on @session. (If
196
 * you don't, then synchronous #GInputStream calls will cause the main
197
 * loop to be run recursively.) The async #GInputStream API works fine
198
 * with %SOUP_SESSION_ASYNC_CONTEXT either set or unset.
199
 *
200
 * Returns: a new #GInputStream.
201
 **/
202
SoupHTTPInputStream *
203
soup_http_input_stream_new (SoupSession *session, SoupMessage *msg)
204
{
205
	SoupHTTPInputStream *stream;
206
	SoupHTTPInputStreamPrivate *priv;
207
208
	g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
209
210
	stream = g_object_new (SOUP_TYPE_HTTP_INPUT_STREAM, NULL);
211
	priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
212
213
	priv->session = g_object_ref (session);
214
	priv->async_context = soup_session_get_async_context (session);
215
	priv->msg = g_object_ref (msg);
216
217
	g_signal_connect (msg, "got_headers",
218
			  G_CALLBACK (soup_http_input_stream_got_headers), stream);
219
	g_signal_connect (msg, "got_chunk",
220
			  G_CALLBACK (soup_http_input_stream_got_chunk), stream);
221
	g_signal_connect (msg, "finished",
222
			  G_CALLBACK (soup_http_input_stream_finished), stream);
223
224
	soup_http_input_stream_queue_message (stream);
225
	return stream;
226
}
227
228
static void
229
soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream)
230
{
231
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
232
233
	/* If the status is unsuccessful, we just ignore the signal and let
234
	 * libsoup keep going (eventually either it will requeue the request
235
	 * (after handling authentication/redirection), or else the
236
	 * "finished" handler will run).
237
	 */
238
	if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
239
		return;
240
241
	priv->got_headers = TRUE;
242
	if (!priv->caller_buffer) {
243
		/* Not ready to read the body yet */
244
		soup_session_pause_message (priv->session, msg);
245
	}
246
247
	if (priv->got_headers_cb)
248
		priv->got_headers_cb (stream);
249
}
250
251
static void
252
soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk_buffer,
253
				  gpointer stream)
254
{
255
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
256
	const gchar *chunk = chunk_buffer->data;
257
	gsize chunk_size = chunk_buffer->length;
258
259
	/* We only pay attention to the chunk if it's part of a successful
260
	 * response.
261
	 */
262
	if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
263
		return;
264
265
	/* Sanity check */
266
	if (priv->caller_bufsize == 0 || priv->leftover_bufsize != 0)
267
		g_warning ("soup_http_input_stream_got_chunk called again before previous chunk was processed");
268
269
	/* Copy what we can into priv->caller_buffer */
270
	if (priv->caller_bufsize - priv->caller_nread > 0) {
271
		gsize nread = MIN (chunk_size, priv->caller_bufsize - priv->caller_nread);
272
273
		memcpy (priv->caller_buffer + priv->caller_nread, chunk, nread);
274
		priv->caller_nread += nread;
275
		priv->offset += nread;
276
		chunk += nread;
277
		chunk_size -= nread;
278
	}
279
280
	if (chunk_size > 0) {
281
		/* Copy the rest into priv->leftover_buffer. If
282
		 * there's already some data there, realloc and
283
		 * append. Otherwise just copy.
284
		 */
285
		if (priv->leftover_bufsize) {
286
			priv->leftover_buffer = g_realloc (priv->leftover_buffer,
287
							   priv->leftover_bufsize + chunk_size);
288
			memcpy (priv->leftover_buffer + priv->leftover_bufsize,
289
				chunk, chunk_size);
290
			priv->leftover_bufsize += chunk_size;
291
		} else {
292
			priv->leftover_bufsize = chunk_size;
293
			priv->leftover_buffer = g_memdup (chunk, chunk_size);
294
			priv->leftover_offset = 0;
295
		}
296
	}
297
298
	soup_session_pause_message (priv->session, msg);
299
	if (priv->got_chunk_cb)
300
		priv->got_chunk_cb (stream);
301
}
302
303
static void
304
soup_http_input_stream_finished (SoupMessage *msg, gpointer stream)
305
{
306
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
307
308
	priv->finished = TRUE;
309
310
	if (priv->finished_cb)
311
		priv->finished_cb (stream);
312
}
313
314
static gboolean
315
soup_http_input_stream_cancelled (GIOChannel *chan, GIOCondition condition,
316
				  gpointer stream)
317
{
318
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
319
320
	priv->cancel_watch = NULL;
321
322
	soup_session_pause_message (priv->session, priv->msg);
323
	if (priv->cancelled_cb)
324
		priv->cancelled_cb (stream);
325
326
	return FALSE;
327
}
328
329
static void
330
soup_http_input_stream_prepare_for_io (GInputStream *stream,
331
				       GCancellable *cancellable,
332
				       guchar       *buffer,
333
				       gsize count)
334
{
335
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
336
	int cancel_fd;
337
338
	priv->cancellable = cancellable;
339
	cancel_fd = g_cancellable_get_fd (cancellable);
340
	if (cancel_fd != -1) {
341
		GIOChannel *chan = g_io_channel_unix_new (cancel_fd);
342
		priv->cancel_watch = soup_add_io_watch (priv->async_context, chan,
343
							G_IO_IN | G_IO_ERR | G_IO_HUP,
344
							soup_http_input_stream_cancelled,
345
							stream);
346
		g_io_channel_unref (chan);
347
	}
348
349
	priv->caller_buffer = buffer;
350
	priv->caller_bufsize = count;
351
	priv->caller_nread = 0;
352
353
	if (priv->got_headers)
354
		soup_session_unpause_message (priv->session, priv->msg);
355
}
356
357
static void
358
soup_http_input_stream_done_io (GInputStream *stream)
359
{
360
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
361
362
	if (priv->cancel_watch) {
363
		g_source_destroy (priv->cancel_watch);
364
		priv->cancel_watch = NULL;
365
		g_cancellable_release_fd (priv->cancellable);
366
	}
367
	priv->cancellable = NULL;
368
369
	priv->caller_buffer = NULL;
370
	priv->caller_bufsize = 0;
371
}
372
373
static gboolean
374
set_error_if_http_failed (SoupMessage *msg, GError **error)
375
{
376
	if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
377
		g_set_error_literal (error, SOUP_HTTP_ERROR,
378
				     msg->status_code, msg->reason_phrase);
379
		return TRUE;
380
	}
381
	return FALSE;
382
}
383
384
static gsize
385
read_from_leftover (SoupHTTPInputStreamPrivate *priv,
386
		    gpointer buffer, gsize bufsize)
387
{
388
	gsize nread;
389
390
	if (priv->leftover_bufsize - priv->leftover_offset <= bufsize) {
391
		nread = priv->leftover_bufsize - priv->leftover_offset;
392
		memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread);
393
394
		g_free (priv->leftover_buffer);
395
		priv->leftover_buffer = NULL;
396
		priv->leftover_bufsize = priv->leftover_offset = 0;
397
	} else {
398
		nread = bufsize;
399
		memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread);
400
		priv->leftover_offset += nread;
401
	}
402
403
	priv->offset += nread;
404
	return nread;
405
}
406
407
/* This does the work of soup_http_input_stream_send(), assuming that the
408
 * GInputStream pending flag has already been set. It is also used by
409
 * soup_http_input_stream_send_async() in some circumstances.
410
 */
411
static gboolean
412
soup_http_input_stream_send_internal (GInputStream  *stream,
413
				      GCancellable  *cancellable,
414
				      GError       **error)
415
{
416
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
417
418
	soup_http_input_stream_prepare_for_io (stream, cancellable, NULL, 0);
419
	while (!priv->finished && !priv->got_headers &&
420
	       !g_cancellable_is_cancelled (cancellable))
421
		g_main_context_iteration (priv->async_context, TRUE);
422
	soup_http_input_stream_done_io (stream);
423
424
	if (g_cancellable_set_error_if_cancelled (cancellable, error))
425
		return FALSE;
426
	else if (set_error_if_http_failed (priv->msg, error))
427
		return FALSE;
428
	return TRUE;
429
}
430
431
static void
432
send_sync_finished (GInputStream *stream)
433
{
434
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
435
	GError *error = NULL;
436
437
	if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error))
438
		set_error_if_http_failed (priv->msg, &error);
439
440
	priv->got_headers_cb = NULL;
441
	priv->finished_cb = NULL;
442
443
	/* Wake up the main context iteration */
444
	g_source_attach (g_idle_source_new (), NULL);
445
}
446
447
/**
448
 * soup_http_input_stream_send:
449
 * @httpstream: a #SoupHTTPInputStream
450
 * @cancellable: optional #GCancellable object, %NULL to ignore.
451
 * @error: location to store the error occuring, or %NULL to ignore
452
 *
453
 * Synchronously sends the HTTP request associated with @stream, and
454
 * reads the response headers. Call this after soup_http_input_stream_new()
455
 * and before the first g_input_stream_read() if you want to check the
456
 * HTTP status code before you start reading.
457
 *
458
 * Return value: %TRUE if msg has a successful (2xx) status, %FALSE if
459
 * not.
460
 **/
461
gboolean
462
soup_http_input_stream_send (SoupHTTPInputStream  *httpstream,
463
			     GCancellable         *cancellable,
464
			     GError              **error)
465
{
466
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream);
467
	GInputStream *istream = (GInputStream *)httpstream;
468
	gboolean result;
469
470
	g_return_val_if_fail (SOUP_IS_HTTP_INPUT_STREAM (httpstream), FALSE);
471
472
	if (!g_input_stream_set_pending (istream, error))
473
		return FALSE;
474
475
	priv->got_headers_cb = send_sync_finished;
476
	priv->finished_cb = send_sync_finished;
477
478
	result = soup_http_input_stream_send_internal (istream, cancellable, error);
479
	g_input_stream_clear_pending (istream);
480
481
	return result;
482
}
483
484
static gssize
485
soup_http_input_stream_read (GInputStream *stream,
486
			     void         *buffer,
487
			     gsize count,
488
			     GCancellable *cancellable,
489
			     GError      **error)
490
{
491
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
492
493
	if (priv->finished)
494
		return 0;
495
496
	/* If there is data leftover from a previous read, return it. */
497
	if (priv->leftover_bufsize)
498
		return read_from_leftover (priv, buffer, count);
499
500
	/* No leftover data, accept one chunk from the network */
501
	soup_http_input_stream_prepare_for_io (stream, cancellable, buffer, count);
502
	while (!priv->finished && priv->caller_nread == 0 &&
503
	       !g_cancellable_is_cancelled (cancellable))
504
		g_main_context_iteration (priv->async_context, TRUE);
505
	soup_http_input_stream_done_io (stream);
506
507
	if (priv->caller_nread > 0)
508
		return priv->caller_nread;
509
510
	if (g_cancellable_set_error_if_cancelled (cancellable, error))
511
		return -1;
512
	else if (set_error_if_http_failed (priv->msg, error))
513
		return -1;
514
	else
515
		return 0;
516
}
517
518
static gboolean
519
soup_http_input_stream_close (GInputStream *stream,
520
			      GCancellable *cancellable,
521
			      GError      **error)
522
{
523
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
524
525
	if (!priv->finished)
526
		soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED);
527
528
	return TRUE;
529
}
530
531
static void
532
wrapper_callback (GObject *source_object, GAsyncResult *res,
533
		  gpointer user_data)
534
{
535
	GInputStream *stream = G_INPUT_STREAM (source_object);
536
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
537
538
	g_input_stream_clear_pending (stream);
539
	if (priv->outstanding_callback)
540
		(*priv->outstanding_callback)(source_object, res, user_data);
541
	priv->outstanding_callback = NULL;
542
	g_object_unref (stream);
543
}
544
545
static void
546
send_async_thread (GSimpleAsyncResult *res,
547
		   GObject *object,
548
		   GCancellable *cancellable)
549
{
550
	GError *error = NULL;
551
	gboolean success;
552
553
	success = soup_http_input_stream_send_internal (G_INPUT_STREAM (object),
554
							cancellable, &error);
555
	g_simple_async_result_set_op_res_gboolean (res, success);
556
	if (error) {
557
		g_simple_async_result_set_from_error (res, error);
558
		g_error_free (error);
559
	}
560
}
561
562
static void
563
soup_http_input_stream_send_async_in_thread (GInputStream        *stream,
564
					     int io_priority,
565
					     GCancellable        *cancellable,
566
					     GAsyncReadyCallback callback,
567
					     gpointer user_data)
568
{
569
	GSimpleAsyncResult *res;
570
571
	res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
572
					 soup_http_input_stream_send_async_in_thread);
573
	g_simple_async_result_run_in_thread (res, send_async_thread,
574
					     io_priority, cancellable);
575
	g_object_unref (res);
576
}
577
578
static void
579
send_async_finished (GInputStream *stream)
580
{
581
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
582
	GSimpleAsyncResult *result;
583
	GError *error = NULL;
584
585
	if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error))
586
		set_error_if_http_failed (priv->msg, &error);
587
588
	priv->got_headers_cb = NULL;
589
	priv->finished_cb = NULL;
590
	soup_http_input_stream_done_io (stream);
591
592
	result = priv->result;
593
	priv->result = NULL;
594
595
	g_simple_async_result_set_op_res_gboolean (result, error == NULL);
596
	if (error) {
597
		g_simple_async_result_set_from_error (result, error);
598
		g_error_free (error);
599
	}
600
	g_simple_async_result_complete (result);
601
}
602
603
static void
604
soup_http_input_stream_send_async_internal (GInputStream        *stream,
605
					    int io_priority,
606
					    GCancellable        *cancellable,
607
					    GAsyncReadyCallback callback,
608
					    gpointer user_data)
609
{
610
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
611
612
	g_object_ref (stream);
613
	priv->outstanding_callback = callback;
614
615
	/* If the session uses the default GMainContext, then we can do
616
	 * async I/O directly. But if it has its own main context, it's
617
	 * easier to just run it in another thread.
618
	 */
619
	if (soup_session_get_async_context (priv->session)) {
620
		soup_http_input_stream_send_async_in_thread (stream, io_priority, cancellable,
621
							     wrapper_callback, user_data);
622
		return;
623
	}
624
625
	priv->got_headers_cb = send_async_finished;
626
	priv->finished_cb = send_async_finished;
627
628
	soup_http_input_stream_prepare_for_io (stream, cancellable, NULL, 0);
629
	priv->result = g_simple_async_result_new (G_OBJECT (stream),
630
						  wrapper_callback, user_data,
631
						  soup_http_input_stream_send_async);
632
}
633
634
/**
635
 * soup_http_input_stream_send_async:
636
 * @httpstream: a #SoupHTTPInputStream
637
 * @io_priority: the io priority of the request.
638
 * @cancellable: optional #GCancellable object, %NULL to ignore.
639
 * @callback: callback to call when the request is satisfied
640
 * @user_data: the data to pass to callback function
641
 *
642
 * Asynchronously sends the HTTP request associated with @stream, and
643
 * reads the response headers. Call this after soup_http_input_stream_new()
644
 * and before the first g_input_stream_read_async() if you want to
645
 * check the HTTP status code before you start reading.
646
 **/
647
void
648
soup_http_input_stream_send_async (SoupHTTPInputStream *httpstream,
649
				   int io_priority,
650
				   GCancellable        *cancellable,
651
				   GAsyncReadyCallback callback,
652
				   gpointer user_data)
653
{
654
	GInputStream *istream = (GInputStream *)httpstream;
655
	GError *error = NULL;
656
657
	g_return_if_fail (SOUP_IS_HTTP_INPUT_STREAM (httpstream));
658
659
	if (!g_input_stream_set_pending (istream, &error)) {
660
		g_simple_async_report_gerror_in_idle (G_OBJECT (httpstream),
661
						      callback,
662
						      user_data,
663
						      error);
664
		g_error_free (error);
665
		return;
666
	}
667
	soup_http_input_stream_send_async_internal (istream, io_priority, cancellable,
668
						    callback, user_data);
669
}
670
671
/**
672
 * soup_http_input_stream_send_finish:
673
 * @httpstream: a #SoupHTTPInputStream
674
 * @result: a #GAsyncResult.
675
 * @error: a #GError location to store the error occuring, or %NULL to
676
 * ignore.
677
 *
678
 * Finishes a soup_http_input_stream_send_async() operation.
679
 *
680
 * Return value: %TRUE if the message was sent successfully and
681
 * received a successful status code, %FALSE if not.
682
 **/
683
gboolean
684
soup_http_input_stream_send_finish (SoupHTTPInputStream  *httpstream,
685
				    GAsyncResult         *result,
686
				    GError              **error)
687
{
688
	GSimpleAsyncResult *simple;
689
690
	g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
691
	simple = G_SIMPLE_ASYNC_RESULT (result);
692
693
	g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == soup_http_input_stream_send_async, FALSE);
694
695
	if (g_simple_async_result_propagate_error (simple, error))
696
		return FALSE;
697
698
	return g_simple_async_result_get_op_res_gboolean (simple);
699
}
700
701
static void
702
read_async_done (GInputStream *stream)
703
{
704
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
705
	GSimpleAsyncResult *result;
706
	GError *error = NULL;
707
708
	result = priv->result;
709
	priv->result = NULL;
710
711
	if (g_cancellable_set_error_if_cancelled (priv->cancellable, &error) ||
712
	    set_error_if_http_failed (priv->msg, &error)) {
713
		g_simple_async_result_set_from_error (result, error);
714
		g_error_free (error);
715
	} else
716
		g_simple_async_result_set_op_res_gssize (result, priv->caller_nread);
717
718
	priv->got_chunk_cb = NULL;
719
	priv->finished_cb = NULL;
720
	priv->cancelled_cb = NULL;
721
	soup_http_input_stream_done_io (stream);
722
723
	g_simple_async_result_complete (result);
724
	g_object_unref (result);
725
}
726
727
static void
728
soup_http_input_stream_read_async (GInputStream        *stream,
729
				   void                *buffer,
730
				   gsize count,
731
				   int io_priority,
732
				   GCancellable        *cancellable,
733
				   GAsyncReadyCallback callback,
734
				   gpointer user_data)
735
{
736
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
737
	GSimpleAsyncResult *result;
738
739
	/* If the session uses the default GMainContext, then we can do
740
	 * async I/O directly. But if it has its own main context, we fall
741
	 * back to the async-via-sync-in-another-thread implementation.
742
	 */
743
	if (soup_session_get_async_context (priv->session)) {
744
		G_INPUT_STREAM_CLASS (soup_http_input_stream_parent_class)->
745
		read_async (stream, buffer, count, io_priority,
746
			    cancellable, callback, user_data);
747
		return;
748
	}
749
750
	result = g_simple_async_result_new (G_OBJECT (stream),
751
					    callback, user_data,
752
					    soup_http_input_stream_read_async);
753
754
	if (priv->finished) {
755
		g_simple_async_result_set_op_res_gssize (result, 0);
756
		g_simple_async_result_complete_in_idle (result);
757
		g_object_unref (result);
758
		return;
759
	}
760
761
	if (priv->leftover_bufsize) {
762
		gsize nread = read_from_leftover (priv, buffer, count);
763
		g_simple_async_result_set_op_res_gssize (result, nread);
764
		g_simple_async_result_complete_in_idle (result);
765
		g_object_unref (result);
766
		return;
767
	}
768
769
	priv->result = result;
770
771
	priv->got_chunk_cb = read_async_done;
772
	priv->finished_cb = read_async_done;
773
	priv->cancelled_cb = read_async_done;
774
	soup_http_input_stream_prepare_for_io (stream, cancellable, buffer, count);
775
}
776
777
static gssize
778
soup_http_input_stream_read_finish (GInputStream  *stream,
779
				    GAsyncResult  *result,
780
				    GError       **error)
781
{
782
	GSimpleAsyncResult *simple;
783
784
	g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), -1);
785
	simple = G_SIMPLE_ASYNC_RESULT (result);
786
	g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == soup_http_input_stream_read_async, -1);
787
788
	return g_simple_async_result_get_op_res_gssize (simple);
789
}
790
791
static void
792
soup_http_input_stream_close_async (GInputStream       *stream,
793
				    int io_priority,
794
				    GCancellable       *cancellable,
795
				    GAsyncReadyCallback callback,
796
				    gpointer user_data)
797
{
798
	GSimpleAsyncResult *result;
799
	gboolean success;
800
	GError *error = NULL;
801
802
	result = g_simple_async_result_new (G_OBJECT (stream),
803
					    callback, user_data,
804
					    soup_http_input_stream_close_async);
805
	success = soup_http_input_stream_close (stream, cancellable, &error);
806
	g_simple_async_result_set_op_res_gboolean (result, success);
807
	if (error) {
808
		g_simple_async_result_set_from_error (result, error);
809
		g_error_free (error);
810
	}
811
812
	g_simple_async_result_complete_in_idle (result);
813
	g_object_unref (result);
814
}
815
816
static gboolean
817
soup_http_input_stream_close_finish (GInputStream  *stream,
818
				     GAsyncResult  *result,
819
				     GError       **error)
820
{
821
	/* Failures handled in generic close_finish code */
822
	return TRUE;
823
}
824
825
static goffset
826
soup_http_input_stream_tell (GSeekable *seekable)
827
{
828
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable);
829
830
	return priv->offset;
831
}
832
833
static gboolean
834
soup_http_input_stream_can_seek (GSeekable *seekable)
835
{
836
	return TRUE;
837
}
838
839
extern void soup_message_io_cleanup (SoupMessage *msg);
840
841
static gboolean
842
soup_http_input_stream_seek (GSeekable     *seekable,
843
			     goffset offset,
844
			     GSeekType type,
845
			     GCancellable  *cancellable,
846
			     GError       **error)
847
{
848
	GInputStream *stream = G_INPUT_STREAM (seekable);
849
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable);
850
	char *range;
851
852
	if (type == G_SEEK_END) {
853
		/* FIXME: we could send "bytes=-offset", but unless we
854
		 * know the Content-Length, we wouldn't be able to
855
		 * answer a tell() properly. We could find the
856
		 * Content-Length by doing a HEAD...
857
		 */
858
859
		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
860
				     "G_SEEK_END not currently supported");
861
		return FALSE;
862
	}
863
864
	if (!g_input_stream_set_pending (stream, error))
865
		return FALSE;
866
867
	soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED);
868
	soup_message_io_cleanup (priv->msg);
869
870
	switch (type) {
871
		case G_SEEK_CUR:
872
			offset += priv->offset;
873
		/* fall through */
874
875
		case G_SEEK_SET:
876
			range = g_strdup_printf ("bytes=%" G_GUINT64_FORMAT "-", (guint64)offset);
877
			priv->offset = offset;
878
			break;
879
880
		case G_SEEK_END:
881
			range = NULL; /* keep compilers happy */
882
			g_return_val_if_reached (FALSE);
883
			break;
884
885
		default:
886
			g_return_val_if_reached (FALSE);
887
	}
888
889
	soup_message_headers_remove (priv->msg->request_headers, "Range");
890
	soup_message_headers_append (priv->msg->request_headers, "Range", range);
891
	g_free (range);
892
893
	soup_http_input_stream_queue_message (SOUP_HTTP_INPUT_STREAM (stream));
894
895
	g_input_stream_clear_pending (stream);
896
	return TRUE;
897
}
898
899
static gboolean
900
soup_http_input_stream_can_truncate (GSeekable *seekable)
901
{
902
	return FALSE;
903
}
904
905
static gboolean
906
soup_http_input_stream_truncate (GSeekable     *seekable,
907
				 goffset offset,
908
				 GCancellable  *cancellable,
909
				 GError       **error)
910
{
911
	g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
912
			     "Truncate not allowed on input stream");
913
	return FALSE;
914
}
915
916
SoupMessage *
917
soup_http_input_stream_get_message (SoupHTTPInputStream *httpstream)
918
{
919
	SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream);
920
	return priv->msg ? g_object_ref (priv->msg) : NULL;
921
}
- a/WebCore/platform/network/soup/cache/soup-http-input-stream.h +76 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-http-input-stream.h_sec1
1
/*
2
 * Copyright (C) 2006, 2007, 2009 Red Hat, Inc.
3
 * Copyright (C) 2010 Igalia, S.L.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General
16
 * Public License along with this library; if not, write to the
17
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18
 * Boston, MA 02111-1307, USA.
19
 */
20
21
#ifndef __SOUP_HTTP_INPUT_STREAM_H__
22
#define __SOUP_HTTP_INPUT_STREAM_H__
23
24
#include <gio/gio.h>
25
#include <libsoup/soup-types.h>
26
27
G_BEGIN_DECLS
28
29
#define SOUP_TYPE_HTTP_INPUT_STREAM         (soup_http_input_stream_get_type ())
30
#define SOUP_HTTP_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), SOUP_TYPE_HTTP_INPUT_STREAM, SoupHTTPInputStream))
31
#define SOUP_HTTP_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), SOUP_TYPE_HTTP_INPUT_STREAM, SoupHTTPInputStreamClass))
32
#define SOUP_IS_HTTP_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), SOUP_TYPE_HTTP_INPUT_STREAM))
33
#define SOUP_IS_HTTP_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), SOUP_TYPE_HTTP_INPUT_STREAM))
34
#define SOUP_HTTP_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SOUP_TYPE_HTTP_INPUT_STREAM, SoupHTTPInputStreamClass))
35
36
typedef struct SoupHTTPInputStream SoupHTTPInputStream;
37
typedef struct SoupHTTPInputStreamClass SoupHTTPInputStreamClass;
38
39
struct SoupHTTPInputStream {
40
	GInputStream parent;
41
};
42
43
struct SoupHTTPInputStreamClass {
44
	GInputStreamClass parent_class;
45
46
	/* Padding for future expansion */
47
	void (*_g_reserved1)(void);
48
	void (*_g_reserved2)(void);
49
	void (*_g_reserved3)(void);
50
	void (*_g_reserved4)(void);
51
	void (*_g_reserved5)(void);
52
};
53
54
GType soup_http_input_stream_get_type (void) G_GNUC_CONST;
55
56
SoupHTTPInputStream *soup_http_input_stream_new (SoupSession         *session,
57
						 SoupMessage         *msg);
58
59
gboolean             soup_http_input_stream_send (SoupHTTPInputStream *httpstream,
60
						  GCancellable        *cancellable,
61
						  GError             **error);
62
63
void                 soup_http_input_stream_send_async (SoupHTTPInputStream *httpstream,
64
							int io_priority,
65
							GCancellable        *cancellable,
66
							GAsyncReadyCallback callback,
67
							gpointer user_data);
68
gboolean             soup_http_input_stream_send_finish (SoupHTTPInputStream *httpstream,
69
							 GAsyncResult        *result,
70
							 GError             **error);
71
72
SoupMessage         *soup_http_input_stream_get_message (SoupHTTPInputStream *httpstream);
73
74
G_END_DECLS
75
76
#endif /* __SOUP_HTTP_INPUT_STREAM_H__ */
- a/WebCore/platform/network/soup/cache/soup-request-data.c +205 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-request-data.c_sec1
1
/*
2
 * soup-request-data.c: data: URI request object
3
 *
4
 * Copyright (C) 2009 Red Hat, Inc.
5
 * Copyright (C) 2010 Igalia, S.L.
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Library General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Library General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Library General Public License
18
 * along with this library; see the file COPYING.LIB.  If not, write to
19
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20
 * Boston, MA 02110-1301, USA.
21
 */
22
23
#ifdef HAVE_CONFIG_H
24
#include <config.h>
25
#endif
26
#include "soup-request-data.h"
27
28
#include "soup-requester.h"
29
#include <libsoup/soup.h>
30
#include <glib/gi18n.h>
31
32
G_DEFINE_TYPE (SoupRequestData, soup_request_data, SOUP_TYPE_REQUEST)
33
34
struct _SoupRequestDataPrivate {
35
	gsize content_length;
36
	char *content_type;
37
};
38
39
static void
40
soup_request_data_init (SoupRequestData *data)
41
{
42
	data->priv = G_TYPE_INSTANCE_GET_PRIVATE (data, SOUP_TYPE_REQUEST_DATA, SoupRequestDataPrivate);
43
}
44
45
static void
46
soup_request_data_finalize (GObject *object)
47
{
48
	SoupRequestData *data = SOUP_REQUEST_DATA (object);
49
50
	g_free (data->priv->content_type);
51
52
	G_OBJECT_CLASS (soup_request_data_parent_class)->finalize (object);
53
}
54
55
static gboolean
56
soup_request_data_check_uri (SoupRequest  *request,
57
			     SoupURI      *uri,
58
			     GError      **error)
59
{
60
	return uri->host == NULL;
61
}
62
63
64
#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
65
#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
66
67
char *
68
uri_decoded_copy (const char *part, int length, gboolean fixup)
69
{
70
	unsigned char *s, *d;
71
	char *decoded = g_strndup (part, length);
72
73
	s = d = (unsigned char *)decoded;
74
	do {
75
		if (*s == '%') {
76
			if (!g_ascii_isxdigit (s[1]) ||
77
			    !g_ascii_isxdigit (s[2])) {
78
				if (!fixup) {
79
					g_free (decoded);
80
					return NULL;
81
				}
82
				*d++ = *s;
83
				continue;
84
			}
85
			*d++ = HEXCHAR (s);
86
			s += 2;
87
		} else
88
			*d++ = *s;
89
	} while (*s++);
90
91
	return decoded;
92
}
93
94
static GInputStream *
95
soup_request_data_send (SoupRequest   *request,
96
			GCancellable  *cancellable,
97
			GError       **error)
98
{
99
	SoupRequestData *data = SOUP_REQUEST_DATA (request);
100
	SoupURI *uri = soup_request_get_uri (request);
101
	GInputStream *memstream;
102
	const char *comma, *semi, *start, *end;
103
	gboolean base64 = FALSE;
104
105
	gchar *uristr = soup_uri_to_string (uri, FALSE);
106
	comma = strchr (uristr, ',');
107
	if (comma && comma != uristr) {
108
		/* Deal with MIME type / params */
109
		semi = memchr (uristr, ';', comma - uristr);
110
		end = semi ? semi : comma;
111
112
		if (end != uristr)
113
			data->priv->content_type = uri_decoded_copy (uristr, end - uristr, TRUE);
114
115
		if (semi && !g_ascii_strncasecmp (semi, ";base64", MAX (comma - semi, strlen (";base64"))))
116
			base64 = TRUE;
117
	}
118
119
	memstream = g_memory_input_stream_new ();
120
121
	start = comma ? comma + 1 : uristr;
122
123
	if (*start) {
124
		guchar *buf;
125
126
		if (base64) {
127
			int inlen, state = 0;
128
			guint save = 0;
129
			char *unescaped;
130
131
			if (strchr (start, '%')) {
132
				start = unescaped = uri_decoded_copy (start, strlen (start), TRUE);
133
				if (!unescaped)
134
					goto fail;
135
			} else
136
				unescaped = NULL;
137
138
			inlen = strlen (start);
139
			buf = g_malloc (inlen * 3 / 4);
140
			data->priv->content_length =
141
				g_base64_decode_step (start, inlen, buf,
142
						      &state, &save);
143
			g_free (unescaped);
144
			if (state != 0) {
145
				g_free (buf);
146
				goto fail;
147
			}
148
		} else {
149
			/* Cannot use g_uri_unescape_string nor
150
			   soup_uri_decode because we don't want to
151
			   fail for things like "%3E%%3C" -> ">%<" */
152
			buf = (guchar *)uri_decoded_copy (start, strlen (start), TRUE);
153
			if (!buf)
154
				goto fail;
155
			data->priv->content_length = strlen ((char *)buf);
156
		}
157
158
		g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (memstream),
159
						buf, data->priv->content_length,
160
						g_free);
161
	}
162
	g_free (uristr);
163
164
	return memstream;
165
166
 fail:
167
	g_free (uristr);
168
	g_set_error (error, SOUP_ERROR, SOUP_ERROR_BAD_URI,
169
		     _ ("Unable to decode URI: %s"), start);
170
	g_object_unref (memstream);
171
	return NULL;
172
}
173
174
static goffset
175
soup_request_data_get_content_length (SoupRequest *request)
176
{
177
	SoupRequestData *data = SOUP_REQUEST_DATA (request);
178
179
	return data->priv->content_length;
180
}
181
182
static const char *
183
soup_request_data_get_content_type (SoupRequest *request)
184
{
185
	SoupRequestData *data = SOUP_REQUEST_DATA (request);
186
187
	return data->priv->content_type;
188
}
189
190
static void
191
soup_request_data_class_init (SoupRequestDataClass *request_data_class)
192
{
193
	GObjectClass *object_class = G_OBJECT_CLASS (request_data_class);
194
	SoupRequestClass *request_class =
195
		SOUP_REQUEST_CLASS (request_data_class);
196
197
	g_type_class_add_private (request_data_class, sizeof (SoupRequestDataPrivate));
198
199
	object_class->finalize = soup_request_data_finalize;
200
201
	request_class->check_uri = soup_request_data_check_uri;
202
	request_class->send = soup_request_data_send;
203
	request_class->get_content_length = soup_request_data_get_content_length;
204
	request_class->get_content_type = soup_request_data_get_content_type;
205
}
- a/WebCore/platform/network/soup/cache/soup-request-data.h +51 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-request-data.h_sec1
1
/*
2
 * Copyright (C) 2009 Red Hat, Inc.
3
 * Copyright (C) 2010 Igalia, S.L.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Library General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Library General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Library General Public License
16
 * along with this library; see the file COPYING.LIB.  If not, write to
17
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
 * Boston, MA 02110-1301, USA.
19
 */
20
21
#ifndef SOUP_REQUEST_DATA_H
22
#define SOUP_REQUEST_DATA_H 1
23
24
#include "soup-request.h"
25
26
G_BEGIN_DECLS
27
28
#define SOUP_TYPE_REQUEST_DATA            (soup_request_data_get_type ())
29
#define SOUP_REQUEST_DATA(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_REQUEST_DATA, SoupRequestData))
30
#define SOUP_REQUEST_DATA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_REQUEST_DATA, SoupRequestDataClass))
31
#define SOUP_IS_REQUEST_DATA(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_REQUEST_DATA))
32
#define SOUP_IS_REQUEST_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_REQUEST_DATA))
33
#define SOUP_REQUEST_DATA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_REQUEST_DATA, SoupRequestDataClass))
34
35
typedef struct _SoupRequestDataPrivate SoupRequestDataPrivate;
36
37
typedef struct {
38
	SoupRequest parent;
39
40
	SoupRequestDataPrivate *priv;
41
} SoupRequestData;
42
43
typedef struct {
44
	SoupRequestClass parent;
45
} SoupRequestDataClass;
46
47
GType soup_request_data_get_type (void);
48
49
G_END_DECLS
50
51
#endif /* SOUP_REQUEST_DATA_H */
- a/WebCore/platform/network/soup/cache/soup-request-file.c +313 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-request-file.c_sec1
1
/*
2
 * soup-request-file.c: file: URI request object
3
 *
4
 * Copyright (C) 2009 Red Hat, Inc.
5
 * Copyright (C) 2010 Igalia, S.L.
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Library General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Library General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Library General Public License
18
 * along with this library; see the file COPYING.LIB.  If not, write to
19
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20
 * Boston, MA 02110-1301, USA.
21
 */
22
23
#ifdef HAVE_CONFIG_H
24
#include <config.h>
25
#endif
26
27
#include "soup-request-file.h"
28
#include "soup-directory-input-stream.h"
29
#include "soup-requester.h"
30
#include <glib/gi18n.h>
31
32
G_DEFINE_TYPE (SoupRequestFile, soup_request_file, SOUP_TYPE_REQUEST)
33
34
struct _SoupRequestFilePrivate {
35
	GFile *gfile;
36
37
	char *mime_type;
38
	goffset size;
39
};
40
41
GFile *
42
soup_request_file_get_file (SoupRequestFile *file)
43
{
44
	return g_object_ref (file->priv->gfile);
45
}
46
47
static void
48
soup_request_file_init (SoupRequestFile *file)
49
{
50
	file->priv = G_TYPE_INSTANCE_GET_PRIVATE (file, SOUP_TYPE_REQUEST_FILE, SoupRequestFilePrivate);
51
52
	file->priv->size = -1;
53
}
54
55
static void
56
soup_request_file_finalize (GObject *object)
57
{
58
	SoupRequestFile *file = SOUP_REQUEST_FILE (object);
59
60
	if (file->priv->gfile)
61
		g_object_unref (file->priv->gfile);
62
	g_free (file->priv->mime_type);
63
64
	G_OBJECT_CLASS (soup_request_file_parent_class)->finalize (object);
65
}
66
67
static gboolean
68
soup_request_file_check_uri (SoupRequest  *request,
69
			     SoupURI      *uri,
70
			     GError      **error)
71
{
72
	/* "file:/foo" is not valid */
73
	if (!uri->host)
74
		return FALSE;
75
76
	/* but it must be "file:///..." or "file://localhost/..." */
77
	if (uri->scheme == SOUP_URI_SCHEME_FILE &&
78
	    *uri->host &&
79
	    g_ascii_strcasecmp (uri->host, "localhost") != 0)
80
		return FALSE;
81
82
	return TRUE;
83
}
84
85
static void
86
soup_request_file_ftp_main_loop_quit (GObject      *object,
87
				      GAsyncResult *result,
88
				      gpointer loop)
89
{
90
	g_main_loop_quit (loop);
91
}
92
93
/* This is a somewhat hacky way to get FTP to almost work. The proper way to
94
 * get FTP to _really_ work involves hacking GIO to have APIs to handle
95
 * canoncial URLs.
96
 */
97
static GFile *
98
soup_request_file_ensure_file_ftp (SoupURI       *uri,
99
				   GCancellable  *cancellable,
100
				   GError       **error)
101
{
102
	SoupURI *host;
103
	char *s;
104
	GFile *file, *result;
105
	GMount *mount;
106
107
	host = soup_uri_copy_host (uri);
108
	s = soup_uri_to_string (host, FALSE);
109
	file = g_file_new_for_uri (s);
110
	soup_uri_free (host);
111
	g_free (s);
112
113
	mount = g_file_find_enclosing_mount (file, cancellable, error);
114
	if (mount == NULL && g_file_supports_thread_contexts (file)) {
115
		GMainContext *context = g_main_context_new ();
116
		GMainLoop *loop = g_main_loop_new (context, FALSE);
117
118
		g_clear_error (error);
119
		g_main_context_push_thread_default (context);
120
		g_file_mount_enclosing_volume (file,
121
					       G_MOUNT_MOUNT_NONE,
122
					       NULL, /* FIXME! */
123
					       cancellable,
124
					       soup_request_file_ftp_main_loop_quit,
125
					       loop);
126
		g_main_loop_run (loop);
127
		g_main_context_pop_thread_default (context);
128
		g_main_loop_unref (loop);
129
		g_main_context_unref (context);
130
		mount = g_file_find_enclosing_mount (file, cancellable, error);
131
	}
132
	if (mount == NULL)
133
		return NULL;
134
	g_object_unref (file);
135
136
	file = g_mount_get_default_location (mount);
137
	g_object_unref (mount);
138
139
	s = g_strdup (uri->path);
140
	if (strchr (s, ';'))
141
		*strchr (s, ';') = 0;
142
143
	result = g_file_resolve_relative_path (file, s);
144
	g_free (s);
145
	g_object_unref (file);
146
147
	return result;
148
}
149
150
static gboolean
151
soup_request_file_ensure_file (SoupRequestFile  *file,
152
			       GCancellable     *cancellable,
153
			       GError          **error)
154
{
155
	SoupURI *uri;
156
157
	if (file->priv->gfile)
158
		return TRUE;
159
160
	uri = soup_request_get_uri (SOUP_REQUEST (file));
161
	if (uri->scheme == SOUP_URI_SCHEME_FILE) {
162
		file->priv->gfile = g_file_new_for_path (soup_uri_decode (uri->path));
163
		return TRUE;
164
	} else if (uri->scheme == SOUP_URI_SCHEME_FTP) {
165
		file->priv->gfile = soup_request_file_ensure_file_ftp (uri,
166
								       cancellable,
167
								       error);
168
		return file->priv->gfile != NULL;
169
	}
170
171
	g_set_error (error, SOUP_ERROR, SOUP_ERROR_UNSUPPORTED_URI_SCHEME,
172
		     _ ("Unsupported URI scheme '%s'"), uri->scheme);
173
	return FALSE;
174
}
175
176
static GInputStream *
177
soup_request_file_send (SoupRequest          *request,
178
			GCancellable         *cancellable,
179
			GError              **error)
180
{
181
	SoupRequestFile *file = SOUP_REQUEST_FILE (request);
182
	GInputStream *stream;
183
	GError *my_error = NULL;
184
185
	if (!soup_request_file_ensure_file (file, cancellable, error))
186
		return NULL;
187
188
	stream = G_INPUT_STREAM (g_file_read (file->priv->gfile,
189
					      cancellable, &my_error));
190
	if (stream == NULL) {
191
		if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)) {
192
			GFileEnumerator *enumerator;
193
			g_clear_error (&my_error);
194
			enumerator = g_file_enumerate_children (file->priv->gfile,
195
								"*",
196
								G_FILE_QUERY_INFO_NONE,
197
								cancellable,
198
								error);
199
			if (enumerator) {
200
				stream = soup_directory_input_stream_new (enumerator,
201
									  soup_request_get_uri (request));
202
				g_object_unref (enumerator);
203
				file->priv->mime_type = g_strdup ("text/html");
204
			}
205
		} else {
206
			g_propagate_error (error, my_error);
207
		}
208
	} else {
209
		GFileInfo *info = g_file_query_info (file->priv->gfile,
210
						     G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
211
						     G_FILE_ATTRIBUTE_STANDARD_SIZE,
212
						     0, cancellable, NULL);
213
		if (info) {
214
			const char *content_type;
215
			file->priv->size = g_file_info_get_size (info);
216
			content_type = g_file_info_get_content_type (info);
217
218
			if (content_type)
219
				file->priv->mime_type = g_content_type_get_mime_type (content_type);
220
			g_object_unref (info);
221
		}
222
	}
223
224
	return stream;
225
}
226
227
static void
228
soup_request_file_send_async_thread (GSimpleAsyncResult *res,
229
				     GObject            *object,
230
				     GCancellable       *cancellable)
231
{
232
	GInputStream *stream;
233
	SoupRequest *request;
234
	GError *error = NULL;
235
236
	request = SOUP_REQUEST (object);
237
238
	stream = soup_request_file_send (request, cancellable, &error);
239
240
	if (stream == NULL) {
241
		g_simple_async_result_set_from_error (res, error);
242
		g_error_free (error);
243
	} else {
244
		g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
245
	}
246
}
247
248
static void
249
soup_request_file_send_async (SoupRequest          *request,
250
			      GCancellable         *cancellable,
251
			      GAsyncReadyCallback callback,
252
			      gpointer user_data)
253
{
254
	GSimpleAsyncResult *res;
255
256
	res = g_simple_async_result_new (G_OBJECT (request), callback, user_data, soup_request_file_send_async);
257
258
	g_simple_async_result_run_in_thread (res, soup_request_file_send_async_thread, G_PRIORITY_DEFAULT, cancellable);
259
	g_object_unref (res);
260
}
261
262
static GInputStream *
263
soup_request_file_send_finish (SoupRequest          *request,
264
			       GAsyncResult         *result,
265
			       GError              **error)
266
{
267
	GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
268
269
	g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == soup_request_file_send_async);
270
271
	if (g_simple_async_result_propagate_error (simple, error))
272
		return NULL;
273
274
	return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
275
}
276
277
static goffset
278
soup_request_file_get_content_length (SoupRequest *request)
279
{
280
	SoupRequestFile *file = SOUP_REQUEST_FILE (request);
281
282
	return file->priv->size;
283
}
284
285
static const char *
286
soup_request_file_get_content_type (SoupRequest *request)
287
{
288
	SoupRequestFile *file = SOUP_REQUEST_FILE (request);
289
290
	if (!file->priv->mime_type)
291
		return "application/octet-stream";
292
293
	return file->priv->mime_type;
294
}
295
296
static void
297
soup_request_file_class_init (SoupRequestFileClass *request_file_class)
298
{
299
	GObjectClass *object_class = G_OBJECT_CLASS (request_file_class);
300
	SoupRequestClass *request_class =
301
		SOUP_REQUEST_CLASS (request_file_class);
302
303
	g_type_class_add_private (request_file_class, sizeof (SoupRequestFilePrivate));
304
305
	object_class->finalize = soup_request_file_finalize;
306
307
	request_class->check_uri = soup_request_file_check_uri;
308
	request_class->send = soup_request_file_send;
309
	request_class->send_async = soup_request_file_send_async;
310
	request_class->send_finish = soup_request_file_send_finish;
311
	request_class->get_content_length = soup_request_file_get_content_length;
312
	request_class->get_content_type = soup_request_file_get_content_type;
313
}
- a/WebCore/platform/network/soup/cache/soup-request-file.h +53 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-request-file.h_sec1
1
/*
2
 * Copyright (C) 2009 Red Hat, Inc.
3
 * Copyright (C) 2010 Igalia, S.L.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Library General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Library General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Library General Public License
16
 * along with this library; see the file COPYING.LIB.  If not, write to
17
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
 * Boston, MA 02110-1301, USA.
19
 */
20
21
#ifndef SOUP_REQUEST_FILE_H
22
#define SOUP_REQUEST_FILE_H 1
23
24
#include "soup-request.h"
25
26
G_BEGIN_DECLS
27
28
#define SOUP_TYPE_REQUEST_FILE            (soup_request_file_get_type ())
29
#define SOUP_REQUEST_FILE(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_REQUEST_FILE, SoupRequestFile))
30
#define SOUP_REQUEST_FILE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_REQUEST_FILE, SoupRequestFileClass))
31
#define SOUP_IS_REQUEST_FILE(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_REQUEST_FILE))
32
#define SOUP_IS_REQUEST_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_REQUEST_FILE))
33
#define SOUP_REQUEST_FILE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_REQUEST_FILE, SoupRequestFileClass))
34
35
typedef struct _SoupRequestFilePrivate SoupRequestFilePrivate;
36
37
typedef struct {
38
	SoupRequest parent;
39
40
	SoupRequestFilePrivate *priv;
41
} SoupRequestFile;
42
43
typedef struct {
44
	SoupRequestClass parent;
45
} SoupRequestFileClass;
46
47
GType soup_request_file_get_type (void);
48
49
GFile  *soup_request_file_get_file (SoupRequestFile *file);
50
51
G_END_DECLS
52
53
#endif /* SOUP_REQUEST_FILE_H */
- a/WebCore/platform/network/soup/cache/soup-request-http.c +339 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-request-http.c_sec1
1
/*
2
 * soup-request-http.c: http: URI request object
3
 *
4
 * Copyright (C) 2009 Red Hat, Inc.
5
 * Copyright (C) 2010 Igalia, S.L.
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Library General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Library General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Library General Public License
18
 * along with this library; see the file COPYING.LIB.  If not, write to
19
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20
 * Boston, MA 02110-1301, USA.
21
 */
22
23
#ifdef HAVE_CONFIG_H
24
#include <config.h>
25
#endif
26
27
#include <glib/gi18n.h>
28
29
#include "soup-cache.h"
30
#include "soup-cache-private.h"
31
#include "soup-http-input-stream.h"
32
#include "soup-request-http.h"
33
34
G_DEFINE_TYPE (SoupRequestHTTP, soup_request_http, SOUP_TYPE_REQUEST)
35
36
struct _SoupRequestHTTPPrivate {
37
	SoupMessage *msg;
38
};
39
40
/**
41
 * soup_request_http_get_message:
42
 * @http: a #SoupRequestHTTP object
43
 *
44
 * Gets a new reference to the #SoupMessage associated to this SoupRequest
45
 *
46
 * Returns: a new reference to the #SoupMessage
47
 **/
48
SoupMessage *
49
soup_request_http_get_message (SoupRequestHTTP *http)
50
{
51
	g_return_val_if_fail (SOUP_IS_REQUEST_HTTP (http), NULL);
52
53
	return g_object_ref (http->priv->msg);
54
}
55
56
static void
57
soup_request_http_init (SoupRequestHTTP *http)
58
{
59
	http->priv = G_TYPE_INSTANCE_GET_PRIVATE (http, SOUP_TYPE_REQUEST_HTTP, SoupRequestHTTPPrivate);
60
}
61
62
static gboolean
63
soup_request_http_check_uri (SoupRequest  *request,
64
			     SoupURI      *uri,
65
			     GError      **error)
66
{
67
	SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
68
69
	if (!SOUP_URI_VALID_FOR_HTTP (uri))
70
		return FALSE;
71
72
	http->priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
73
	return TRUE;
74
}
75
76
static void
77
soup_request_http_finalize (GObject *object)
78
{
79
	SoupRequestHTTP *http = SOUP_REQUEST_HTTP (object);
80
81
	if (http->priv->msg)
82
		g_object_unref (http->priv->msg);
83
84
	G_OBJECT_CLASS (soup_request_http_parent_class)->finalize (object);
85
}
86
87
static GInputStream *
88
soup_request_http_send (SoupRequest          *request,
89
			GCancellable         *cancellable,
90
			GError              **error)
91
{
92
	SoupHTTPInputStream *httpstream;
93
	SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
94
95
	httpstream = soup_http_input_stream_new (soup_request_get_session (request), http->priv->msg);
96
	if (!soup_http_input_stream_send (httpstream, cancellable, error)) {
97
		g_object_unref (httpstream);
98
		return NULL;
99
	}
100
	return (GInputStream *)httpstream;
101
}
102
103
104
static void
105
sent_async (GObject *source, GAsyncResult *result, gpointer user_data)
106
{
107
	SoupHTTPInputStream *httpstream = SOUP_HTTP_INPUT_STREAM (source);
108
	GSimpleAsyncResult *simple = user_data;
109
	GError *error = NULL;
110
111
	if (soup_http_input_stream_send_finish (httpstream, result, &error)) {
112
		g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
113
	} else {
114
		g_simple_async_result_set_from_error (simple, error);
115
		g_error_free (error);
116
		g_object_unref (httpstream);
117
	}
118
	g_simple_async_result_complete (simple);
119
	g_object_unref (simple);
120
}
121
122
123
typedef struct {
124
	SoupRequestHTTP *req;
125
	SoupMessage *original;
126
	GCancellable *cancellable;
127
	GAsyncReadyCallback callback;
128
	gpointer user_data;
129
} ConditionalHelper;
130
131
132
static void
133
conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
134
{
135
	ConditionalHelper *helper = (ConditionalHelper *)user_data;
136
	GSimpleAsyncResult *simple;
137
	SoupHTTPInputStream *httpstream;
138
139
	simple = g_simple_async_result_new (G_OBJECT (helper->req),
140
					    helper->callback, helper->user_data,
141
					    conditional_get_ready_cb);
142
143
	if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
144
		WebKitSoupCache *cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
145
146
		httpstream = (SoupHTTPInputStream *)webkit_soup_cache_send_response (cache, msg);
147
		if (httpstream) {
148
			const gchar *content_type;
149
150
			g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
151
152
			soup_message_got_headers (helper->original);
153
			content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
154
			soup_message_content_sniffed (helper->original, content_type, NULL);
155
156
			g_simple_async_result_complete (simple);
157
158
			soup_message_finished (helper->original);
159
160
			g_object_unref (simple);
161
		} else {
162
			/* Ask again for the resource, somehow the cache cannot locate it */
163
			httpstream = soup_http_input_stream_new (session, helper->original);
164
			soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
165
							   helper->cancellable, sent_async, simple);
166
		}
167
	} else {
168
		/* It is in the cache but it was modified remotely */
169
		httpstream = soup_http_input_stream_new (session, helper->original);
170
		soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
171
						   helper->cancellable, sent_async, simple);
172
	}
173
174
	g_object_unref (helper->req);
175
	g_object_unref (helper->original);
176
	g_slice_free (ConditionalHelper, helper);
177
}
178
179
typedef struct {
180
	SoupRequestHTTP *http;
181
	GAsyncReadyCallback callback;
182
	gpointer user_data;
183
} SendAsyncHelper;
184
185
static void soup_request_http_send_async (SoupRequest          *request,
186
					  GCancellable         *cancellable,
187
					  GAsyncReadyCallback callback,
188
					  gpointer user_data);
189
190
static gboolean
191
send_async_cb (gpointer data)
192
{
193
	GSimpleAsyncResult *simple;
194
	SoupHTTPInputStream *httpstream;
195
	SoupSession *session;
196
	WebKitSoupCache *cache;
197
	SendAsyncHelper *helper = (SendAsyncHelper *)data;
198
199
	session = soup_request_get_session (SOUP_REQUEST (helper->http));
200
	cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
201
202
	httpstream = (SoupHTTPInputStream *)webkit_soup_cache_send_response (cache, SOUP_MESSAGE (helper->http->priv->msg));
203
204
	if (httpstream) {
205
		const gchar *content_type;
206
207
		simple = g_simple_async_result_new (G_OBJECT (helper->http),
208
						    helper->callback, helper->user_data,
209
						    soup_request_http_send_async);
210
		g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
211
212
		/* Update message status */
213
		soup_message_set_status (helper->http->priv->msg, SOUP_STATUS_OK);
214
215
		/* Issue signals  */
216
		soup_message_got_headers (helper->http->priv->msg);
217
		content_type = soup_message_headers_get_content_type (helper->http->priv->msg->response_headers, NULL);
218
		soup_message_content_sniffed (helper->http->priv->msg, content_type, NULL);
219
220
		g_simple_async_result_complete (simple);
221
222
		soup_message_finished (helper->http->priv->msg);
223
224
		g_object_unref (simple);
225
	}
226
227
	g_slice_free (SendAsyncHelper, helper);
228
229
	return FALSE;
230
}
231
232
static void
233
soup_request_http_send_async (SoupRequest          *request,
234
			      GCancellable         *cancellable,
235
			      GAsyncReadyCallback callback,
236
			      gpointer user_data)
237
{
238
	SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
239
	SoupHTTPInputStream *httpstream;
240
	GSimpleAsyncResult *simple;
241
	SoupSession *session;
242
	WebKitSoupCache *cache;
243
244
	session = soup_request_get_session (request);
245
	cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
246
247
	if (cache) {
248
		WebKitSoupCacheResponse response;
249
250
		response = webkit_soup_cache_has_response (cache, http->priv->msg);
251
		if (response == WEBKIT_SOUP_CACHE_RESPONSE_FRESH) {
252
			/* Do return the stream asynchronously as in
253
			   the other cases. It's not enough to use
254
			   g_simple_async_result_complete_in_idle as
255
			   the signals must be also emitted
256
			   asynchronously */
257
			SendAsyncHelper *helper = g_slice_new (SendAsyncHelper);
258
			helper->http = http;
259
			helper->callback = callback;
260
			helper->user_data = user_data;
261
			g_timeout_add (0, send_async_cb, helper);
262
			return;
263
		} else if (response == WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
264
			SoupMessage *conditional_msg;
265
			ConditionalHelper *helper;
266
267
			conditional_msg = webkit_soup_cache_generate_conditional_request (cache, http->priv->msg);
268
269
			helper = g_slice_new0 (ConditionalHelper);
270
			helper->req = g_object_ref (http);
271
			helper->original = g_object_ref (http->priv->msg);
272
			helper->cancellable = cancellable;
273
			helper->callback = callback;
274
			helper->user_data = user_data;
275
			soup_session_queue_message (session, conditional_msg,
276
						    conditional_get_ready_cb,
277
						    helper);
278
			return;
279
		}
280
	}
281
282
	simple = g_simple_async_result_new (G_OBJECT (http),
283
					    callback, user_data,
284
					    soup_request_http_send_async);
285
	httpstream = soup_http_input_stream_new (soup_request_get_session (request),
286
						 http->priv->msg);
287
	soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
288
					   cancellable, sent_async, simple);
289
}
290
291
static GInputStream *
292
soup_request_http_send_finish (SoupRequest          *request,
293
			       GAsyncResult         *result,
294
			       GError              **error)
295
{
296
	GSimpleAsyncResult *simple;
297
298
	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), soup_request_http_send_async) || g_simple_async_result_is_valid (result, G_OBJECT (request), conditional_get_ready_cb), NULL);
299
300
	simple = G_SIMPLE_ASYNC_RESULT (result);
301
	if (g_simple_async_result_propagate_error (simple, error))
302
		return NULL;
303
	return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
304
}
305
306
static goffset
307
soup_request_http_get_content_length (SoupRequest *request)
308
{
309
	SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
310
311
	return soup_message_headers_get_content_length (http->priv->msg->response_headers);
312
}
313
314
static const char *
315
soup_request_http_get_content_type (SoupRequest *request)
316
{
317
	SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
318
319
	return soup_message_headers_get_content_type (http->priv->msg->response_headers, NULL);
320
}
321
322
static void
323
soup_request_http_class_init (SoupRequestHTTPClass *request_http_class)
324
{
325
	GObjectClass *object_class = G_OBJECT_CLASS (request_http_class);
326
	SoupRequestClass *request_class =
327
		SOUP_REQUEST_CLASS (request_http_class);
328
329
	g_type_class_add_private (request_http_class, sizeof (SoupRequestHTTPPrivate));
330
331
	object_class->finalize = soup_request_http_finalize;
332
333
	request_class->check_uri = soup_request_http_check_uri;
334
	request_class->send = soup_request_http_send;
335
	request_class->send_async = soup_request_http_send_async;
336
	request_class->send_finish = soup_request_http_send_finish;
337
	request_class->get_content_length = soup_request_http_get_content_length;
338
	request_class->get_content_type = soup_request_http_get_content_type;
339
}
- a/WebCore/platform/network/soup/cache/soup-request-http.h +53 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-request-http.h_sec1
1
/*
2
 * Copyright (C) 2009 Red Hat, Inc.
3
 * Copyright (C) 2010 Igalia, S.L.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Library General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Library General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Library General Public License
16
 * along with this library; see the file COPYING.LIB.  If not, write to
17
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
 * Boston, MA 02110-1301, USA.
19
 */
20
21
#ifndef SOUP_REQUEST_HTTP_H
22
#define SOUP_REQUEST_HTTP_H 1
23
24
#include "soup-request.h"
25
26
G_BEGIN_DECLS
27
28
#define SOUP_TYPE_REQUEST_HTTP            (soup_request_http_get_type ())
29
#define SOUP_REQUEST_HTTP(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_REQUEST_HTTP, SoupRequestHTTP))
30
#define SOUP_REQUEST_HTTP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_REQUEST_HTTP, SoupRequestHTTPClass))
31
#define SOUP_IS_REQUEST_HTTP(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_REQUEST_HTTP))
32
#define SOUP_IS_REQUEST_HTTP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_REQUEST_HTTP))
33
#define SOUP_REQUEST_HTTP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_REQUEST_HTTP, SoupRequestHTTPClass))
34
35
typedef struct _SoupRequestHTTPPrivate SoupRequestHTTPPrivate;
36
37
typedef struct {
38
	SoupRequest parent;
39
40
	SoupRequestHTTPPrivate *priv;
41
} SoupRequestHTTP;
42
43
typedef struct {
44
	SoupRequestClass parent;
45
} SoupRequestHTTPClass;
46
47
GType soup_request_http_get_type (void);
48
49
SoupMessage  *soup_request_http_get_message (SoupRequestHTTP *http);
50
51
G_END_DECLS
52
53
#endif /* SOUP_REQUEST_HTTP_H */
- a/WebCore/platform/network/soup/cache/soup-request.c +279 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-request.c_sec1
1
/*
2
 * soup-request.c: Protocol-independent streaming request interface
3
 *
4
 * Copyright (C) 2009 Red Hat, Inc.
5
 * Copyright (C) 2010, Igalia S.L.
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Library General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Library General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Library General Public License
18
 * along with this library; see the file COPYING.LIB.  If not, write to
19
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20
 * Boston, MA 02110-1301, USA.
21
 */
22
23
#ifdef HAVE_CONFIG_H
24
#include <config.h>
25
#endif
26
27
#include <glib/gi18n.h>
28
29
#include "soup-request.h"
30
#include "soup-requester.h"
31
32
/**
33
 * SECTION:soup-request
34
 * @short_description: Protocol-independent streaming request interface
35
 *
36
 * FIXME
37
 **/
38
39
/**
40
 * SoupRequest:
41
 *
42
 * FIXME
43
 *
44
 * Since: 2.30
45
 **/
46
47
static void soup_request_initable_interface_init (GInitableIface *initable_interface);
48
49
G_DEFINE_TYPE_WITH_CODE (SoupRequest, soup_request, G_TYPE_OBJECT,
50
			 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
51
						soup_request_initable_interface_init))
52
53
enum {
54
	PROP_0,
55
	PROP_URI,
56
	PROP_SESSION
57
};
58
59
struct _SoupRequestPrivate {
60
	SoupURI *uri;
61
	SoupSession *session;
62
};
63
64
static void
65
soup_request_init (SoupRequest *request)
66
{
67
	request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, SOUP_TYPE_REQUEST, SoupRequestPrivate);
68
}
69
70
static void
71
soup_request_finalize (GObject *object)
72
{
73
	SoupRequest *request = SOUP_REQUEST (object);
74
75
	if (request->priv->uri)
76
		soup_uri_free (request->priv->uri);
77
	if (request->priv->session)
78
		g_object_unref (request->priv->session);
79
80
	G_OBJECT_CLASS (soup_request_parent_class)->finalize (object);
81
}
82
83
static void
84
soup_request_set_property (GObject      *object,
85
			   guint prop_id,
86
			   const GValue *value,
87
			   GParamSpec   *pspec)
88
{
89
	SoupRequest *request = SOUP_REQUEST (object);
90
91
	switch (prop_id) {
92
		case PROP_URI:
93
			if (request->priv->uri)
94
				soup_uri_free (request->priv->uri);
95
			request->priv->uri = g_value_dup_boxed (value);
96
			break;
97
		case PROP_SESSION:
98
			if (request->priv->session)
99
				g_object_unref (request->priv->session);
100
			request->priv->session = g_value_dup_object (value);
101
			break;
102
		default:
103
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
104
			break;
105
	}
106
}
107
108
static void
109
soup_request_get_property (GObject    *object,
110
			   guint prop_id,
111
			   GValue     *value,
112
			   GParamSpec *pspec)
113
{
114
	SoupRequest *request = SOUP_REQUEST (object);
115
116
	switch (prop_id) {
117
		case PROP_URI:
118
			g_value_set_boxed (value, request->priv->uri);
119
			break;
120
		case PROP_SESSION:
121
			g_value_set_object (value, request->priv->session);
122
			break;
123
		default:
124
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
125
			break;
126
	}
127
}
128
129
static gboolean
130
soup_request_initable_init (GInitable     *initable,
131
			    GCancellable  *cancellable,
132
			    GError       **error)
133
{
134
	SoupRequest *request = SOUP_REQUEST (initable);
135
	gboolean ok;
136
137
	if (!request->priv->uri) {
138
		g_set_error (error, SOUP_ERROR, SOUP_ERROR_BAD_URI,
139
			     _ ("No URI provided"));
140
		return FALSE;
141
	}
142
143
	ok = SOUP_REQUEST_GET_CLASS (initable)->
144
	     check_uri (request, request->priv->uri, error);
145
146
	if (!ok && error) {
147
		char *uri_string = soup_uri_to_string (request->priv->uri, FALSE);
148
		g_set_error (error, SOUP_ERROR, SOUP_ERROR_BAD_URI,
149
			     _ ("Invalid '%s' URI: %s"),
150
			     request->priv->uri->scheme,
151
			     uri_string);
152
		g_free (uri_string);
153
	}
154
155
	return ok;
156
}
157
158
static gboolean
159
soup_request_default_check_uri (SoupRequest  *request,
160
				SoupURI      *uri,
161
				GError      **error)
162
{
163
	return TRUE;
164
}
165
166
/* Default implementation: assume the sync implementation doesn't block */
167
static void
168
soup_request_default_send_async (SoupRequest          *request,
169
				 GCancellable         *cancellable,
170
				 GAsyncReadyCallback callback,
171
				 gpointer user_data)
172
{
173
	GSimpleAsyncResult *simple;
174
175
	simple = g_simple_async_result_new (G_OBJECT (request),
176
					    callback, user_data,
177
					    soup_request_default_send_async);
178
	g_simple_async_result_complete_in_idle (simple);
179
	g_object_unref (simple);
180
}
181
182
static GInputStream *
183
soup_request_default_send_finish (SoupRequest          *request,
184
				  GAsyncResult         *result,
185
				  GError              **error)
186
{
187
	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), soup_request_default_send_async), NULL);
188
189
	return soup_request_send (request, NULL, error);
190
}
191
192
GInputStream *
193
soup_request_send (SoupRequest          *request,
194
		   GCancellable         *cancellable,
195
		   GError              **error)
196
{
197
	return SOUP_REQUEST_GET_CLASS (request)->
198
	       send (request, cancellable, error);
199
}
200
201
void
202
soup_request_send_async (SoupRequest          *request,
203
			 GCancellable         *cancellable,
204
			 GAsyncReadyCallback callback,
205
			 gpointer user_data)
206
{
207
	SOUP_REQUEST_GET_CLASS (request)->
208
	send_async (request, cancellable, callback, user_data);
209
}
210
211
GInputStream *
212
soup_request_send_finish (SoupRequest          *request,
213
			  GAsyncResult         *result,
214
			  GError              **error)
215
{
216
	return SOUP_REQUEST_GET_CLASS (request)->
217
	       send_finish (request, result, error);
218
}
219
220
static void
221
soup_request_class_init (SoupRequestClass *request_class)
222
{
223
	GObjectClass *object_class = G_OBJECT_CLASS (request_class);
224
225
	g_type_class_add_private (request_class, sizeof (SoupRequestPrivate));
226
227
	request_class->check_uri = soup_request_default_check_uri;
228
	request_class->send_async = soup_request_default_send_async;
229
	request_class->send_finish = soup_request_default_send_finish;
230
231
	object_class->finalize = soup_request_finalize;
232
	object_class->set_property = soup_request_set_property;
233
	object_class->get_property = soup_request_get_property;
234
235
	g_object_class_install_property (
236
		object_class, PROP_URI,
237
		g_param_spec_boxed (SOUP_REQUEST_URI,
238
				    "URI",
239
				    "The request URI",
240
				    SOUP_TYPE_URI,
241
				    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
242
	g_object_class_install_property (
243
		object_class, PROP_SESSION,
244
		g_param_spec_object (SOUP_REQUEST_SESSION,
245
				     "Session",
246
				     "The request's session",
247
				     SOUP_TYPE_SESSION,
248
				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
249
}
250
251
static void
252
soup_request_initable_interface_init (GInitableIface *initable_interface)
253
{
254
	initable_interface->init = soup_request_initable_init;
255
}
256
257
SoupURI *
258
soup_request_get_uri (SoupRequest *request)
259
{
260
	return request->priv->uri;
261
}
262
263
SoupSession *
264
soup_request_get_session (SoupRequest *request)
265
{
266
	return request->priv->session;
267
}
268
269
goffset
270
soup_request_get_content_length (SoupRequest *request)
271
{
272
	return SOUP_REQUEST_GET_CLASS (request)->get_content_length (request);
273
}
274
275
const char *
276
soup_request_get_content_type (SoupRequest  *request)
277
{
278
	return SOUP_REQUEST_GET_CLASS (request)->get_content_type (request);
279
}
- a/WebCore/platform/network/soup/cache/soup-request.h +92 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-request.h_sec1
1
/*
2
 * Copyright (C) 2009 Red Hat, Inc.
3
 * Copyright (C) 2010 Igalia, S.L.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Library General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Library General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Library General Public License
16
 * along with this library; see the file COPYING.LIB.  If not, write to
17
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
 * Boston, MA 02110-1301, USA.
19
 */
20
21
#ifndef SOUP_REQUEST_H
22
#define SOUP_REQUEST_H 1
23
24
#include <libsoup/soup.h>
25
#include <gio/gio.h>
26
27
G_BEGIN_DECLS
28
29
#define SOUP_TYPE_REQUEST            (soup_request_get_type ())
30
#define SOUP_REQUEST(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_REQUEST, SoupRequest))
31
#define SOUP_REQUEST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_REQUEST, SoupRequestClass))
32
#define SOUP_IS_REQUEST(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_REQUEST))
33
#define SOUP_IS_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_REQUEST))
34
#define SOUP_REQUEST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_REQUEST, SoupRequestClass))
35
36
typedef struct _SoupRequest SoupRequest;
37
typedef struct _SoupRequestPrivate SoupRequestPrivate;
38
typedef struct _SoupRequestClass SoupRequestClass;
39
40
struct _SoupRequest {
41
	GObject parent;
42
43
	SoupRequestPrivate *priv;
44
};
45
46
struct _SoupRequestClass {
47
	GObjectClass parent;
48
49
	gboolean (*check_uri)(SoupRequest          *req_base,
50
			      SoupURI              *uri,
51
			      GError              **error);
52
53
	GInputStream * (*send)(SoupRequest          *request,
54
			       GCancellable         *cancellable,
55
			       GError              **error);
56
	void (*send_async)(SoupRequest          *request,
57
			   GCancellable         *cancellable,
58
			   GAsyncReadyCallback callback,
59
			   gpointer user_data);
60
	GInputStream * (*send_finish)(SoupRequest          *request,
61
				      GAsyncResult         *result,
62
				      GError              **error);
63
64
	goffset (*get_content_length)(SoupRequest   *request);
65
	const char *   (*get_content_type)(SoupRequest   *request);
66
};
67
68
GType soup_request_get_type (void);
69
70
#define SOUP_REQUEST_URI     "uri"
71
#define SOUP_REQUEST_SESSION "session"
72
73
GInputStream *soup_request_send (SoupRequest          *request,
74
				 GCancellable         *cancellable,
75
				 GError              **error);
76
void          soup_request_send_async (SoupRequest          *request,
77
				       GCancellable         *cancellable,
78
				       GAsyncReadyCallback callback,
79
				       gpointer user_data);
80
GInputStream *soup_request_send_finish (SoupRequest          *request,
81
					GAsyncResult         *result,
82
					GError              **error);
83
84
SoupURI      *soup_request_get_uri (SoupRequest          *request);
85
SoupSession  *soup_request_get_session (SoupRequest          *request);
86
87
goffset       soup_request_get_content_length (SoupRequest          *request);
88
const char   *soup_request_get_content_type (SoupRequest          *request);
89
90
G_END_DECLS
91
92
#endif /* SOUP_REQUEST_H */
- a/WebCore/platform/network/soup/cache/soup-requester.c +186 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-requester.c_sec1
1
/*
2
 * soup-requester.c:
3
 *
4
 * Copyright (C) 2010, Igalia S.L.
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Library General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Library General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Library General Public License
17
 * along with this library; see the file COPYING.LIB.  If not, write to
18
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
 * Boston, MA 02110-1301, USA.
20
 */
21
22
#include "config.h"
23
#include "soup-requester.h"
24
25
#include "soup-request-data.h"
26
#include "soup-request-file.h"
27
#include "soup-request-http.h"
28
#include <glib/gi18n.h>
29
#include <libsoup/soup.h>
30
31
struct _SoupRequesterPrivate {
32
	GHashTable *request_types;
33
};
34
35
#define SOUP_REQUESTER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_REQUESTER, SoupRequesterPrivate))
36
37
G_DEFINE_TYPE (SoupRequester, soup_requester, G_TYPE_OBJECT)
38
39
static void soup_requester_init (SoupRequester *requester)
40
{
41
	requester->priv = SOUP_REQUESTER_GET_PRIVATE (requester);
42
43
	requester->priv->request_types = 0;
44
}
45
46
static void finalize (GObject *object)
47
{
48
	SoupRequester *requester = SOUP_REQUESTER (object);
49
50
	if (requester->priv->request_types)
51
		g_hash_table_destroy (requester->priv->request_types);
52
53
	G_OBJECT_CLASS (soup_requester_parent_class)->finalize (object);
54
}
55
56
static void soup_requester_class_init (SoupRequesterClass *requester_class)
57
{
58
	GObjectClass *object_class = G_OBJECT_CLASS (requester_class);
59
60
	g_type_class_add_private (requester_class, sizeof (SoupRequesterPrivate));
61
62
	/* virtual method override */
63
	object_class->finalize = finalize;
64
}
65
66
static void init_request_types (SoupRequesterPrivate *priv)
67
{
68
	if (priv->request_types)
69
		return;
70
71
	priv->request_types = g_hash_table_new_full (soup_str_case_hash,
72
						     soup_str_case_equal,
73
						     g_free, 0);
74
	g_hash_table_insert (priv->request_types, g_strdup ("file"),
75
			     GSIZE_TO_POINTER (SOUP_TYPE_REQUEST_FILE));
76
	g_hash_table_insert (priv->request_types, g_strdup ("data"),
77
			     GSIZE_TO_POINTER (SOUP_TYPE_REQUEST_DATA));
78
	g_hash_table_insert (priv->request_types, g_strdup ("http"),
79
			     GSIZE_TO_POINTER (SOUP_TYPE_REQUEST_HTTP));
80
	g_hash_table_insert (priv->request_types, g_strdup ("https"),
81
			     GSIZE_TO_POINTER (SOUP_TYPE_REQUEST_HTTP));
82
	g_hash_table_insert (priv->request_types, g_strdup ("ftp"),
83
			     GSIZE_TO_POINTER (SOUP_TYPE_REQUEST_FILE));
84
}
85
86
SoupRequester *soup_requester_new (void)
87
{
88
	return (SoupRequester *)g_object_new (SOUP_TYPE_REQUESTER, 0);
89
}
90
91
SoupRequest *soup_requester_request (SoupRequester *requester, const char *uriString, SoupSession *session, GError **error)
92
{
93
	SoupURI *uri = soup_uri_new (uriString);
94
	SoupRequest *req;
95
96
	if (!uri) {
97
		g_set_error (error, SOUP_ERROR, SOUP_ERROR_BAD_URI,
98
			     _ ("Could not parse URI '%s'"), uriString);
99
		return 0;
100
	}
101
102
	req = soup_requester_request_uri (requester, uri, session, error);
103
	soup_uri_free (uri);
104
	return req;
105
}
106
107
SoupRequest *soup_requester_request_uri (SoupRequester *requester, SoupURI *uri, SoupSession *session, GError **error)
108
{
109
	GType requestType;
110
111
	g_return_val_if_fail (SOUP_IS_REQUESTER (requester), 0);
112
113
	init_request_types (requester->priv);
114
	requestType = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (requester->priv->request_types, uri->scheme));
115
	if (!requestType) {
116
		g_set_error (error, SOUP_ERROR, SOUP_ERROR_UNSUPPORTED_URI_SCHEME,
117
			     _ ("Unsupported URI scheme '%s'"), uri->scheme);
118
		return 0;
119
	}
120
121
	if (g_type_is_a (requestType, G_TYPE_INITABLE)) {
122
		return (SoupRequest *)g_initable_new (requestType, 0, error,
123
						      "uri", uri,
124
						      "session", session,
125
						      0);
126
	} else {
127
		return (SoupRequest *)g_object_new (requestType,
128
						    "uri", uri,
129
						    "session", session,
130
						    0);
131
	}
132
}
133
134
/* RFC 2396, 3.1 */
135
static gboolean
136
soup_scheme_is_valid (const char *scheme)
137
{
138
	if (scheme == NULL ||
139
	    !g_ascii_isalpha (*scheme))
140
		return FALSE;
141
142
	scheme++;
143
	while (*scheme) {
144
		if (!g_ascii_isalpha (*scheme) &&
145
		    !g_ascii_isdigit (*scheme) &&
146
		    *scheme != '+' &&
147
		    *scheme != '-' &&
148
		    *scheme != '.')
149
			return FALSE;
150
		scheme++;
151
	}
152
	return TRUE;
153
}
154
155
void
156
soup_requester_add_protocol (SoupRequester *requester,
157
			     const char  *scheme,
158
			     GType request_type)
159
{
160
	g_return_if_fail (SOUP_IS_REQUESTER (requester));
161
	g_return_if_fail (soup_scheme_is_valid (scheme));
162
163
	init_request_types (requester->priv);
164
	g_hash_table_insert (requester->priv->request_types, g_strdup (scheme),
165
			     GSIZE_TO_POINTER (request_type));
166
}
167
168
void
169
soup_requester_remove_protocol (SoupRequester *requester,
170
				const char  *scheme)
171
{
172
	g_return_if_fail (SOUP_IS_REQUESTER (requester));
173
	g_return_if_fail (soup_scheme_is_valid (scheme));
174
175
	init_request_types (requester->priv);
176
	g_hash_table_remove (requester->priv->request_types, scheme);
177
}
178
179
GQuark
180
soup_error_quark (void)
181
{
182
	static GQuark error;
183
	if (!error)
184
		error = g_quark_from_static_string ("soup_error_quark");
185
	return error;
186
}
- a/WebCore/platform/network/soup/cache/soup-requester.h +80 lines
Line 0 a/WebCore/platform/network/soup/cache/soup-requester.h_sec1
1
/*
2
 * Copyright (C) 2010 Igalia S.L.
3
 *
4
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Library General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2 of the License, or (at your option) any later version.
8
 *
9
 * This library is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Library General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Library General Public License
15
 * along with this library; see the file COPYING.LIB.  If not, write to
16
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
 * Boston, MA 02110-1301, USA.
18
 */
19
20
#ifndef SOUP_REQUESTER_H
21
#define SOUP_REQUESTER_H 1
22
23
#include "soup-request.h"
24
#include <libsoup/soup.h>
25
26
G_BEGIN_DECLS
27
28
#define SOUP_TYPE_REQUESTER            (soup_requester_get_type ())
29
#define SOUP_REQUESTER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_REQUESTER, SoupRequester))
30
#define SOUP_REQUESTER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_REQUESTER, SoupRequesterClass))
31
#define SOUP_IS_REQUESTER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_REQUESTER))
32
#define SOUP_IS_REQUESTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_REQUESTER))
33
#define SOUP_REQUESTER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_REQUESTER, SoupRequesterClass))
34
35
#define SOUP_ERROR soup_error_quark ()
36
37
typedef enum {
38
	SOUP_ERROR_BAD_URI,
39
	SOUP_ERROR_UNSUPPORTED_URI_SCHEME
40
} SoupError;
41
42
typedef struct _SoupRequester SoupRequester;
43
typedef struct _SoupRequesterPrivate SoupRequesterPrivate;
44
45
struct _SoupRequester {
46
	GObject parent;
47
48
	SoupRequesterPrivate *priv;
49
};
50
51
typedef struct {
52
	GObjectClass parent_class;
53
} SoupRequesterClass;
54
55
GType soup_requester_get_type (void);
56
57
SoupRequester *soup_requester_new (void);
58
59
GQuark soup_error_quark (void);
60
61
SoupRequest *soup_requester_request (SoupRequester *requester,
62
				     const char *uriString,
63
				     SoupSession *session,
64
				     GError **error);
65
66
SoupRequest *soup_requester_request_uri (SoupRequester *requester,
67
					 SoupURI *uri,
68
					 SoupSession *session,
69
					 GError **error);
70
71
void soup_requester_add_protocol (SoupRequester *requester,
72
				  const char  *scheme,
73
				  GType request_type);
74
75
void soup_requester_remove_protocol (SoupRequester *requester,
76
				     const char  *scheme);
77
78
G_END_DECLS
79
80
#endif /* SOUP_REQUESTER_H */
- a/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h +41 lines
Line 0 a/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h_sec1
1
/*
2
 * soup-cache-private.h:
3
 *
4
 * Copyright (C) 2010 Igalia, S.L.
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Library General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Library General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Library General Public License
17
 * along with this library; see the file COPYING.LIB.  If not, write to
18
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
 * Boston, MA 02110-1301, USA.
20
 */
21
22
#ifndef WEBKIT_SOUP_CACHE_PRIVATE_H
23
#define WEBKIT_SOUP_CACHE_PRIVATE_H 1
24
25
#include "soup-cache.h"
26
#include <libsoup/soup-message.h>
27
28
G_BEGIN_DECLS
29
30
WebKitSoupCacheResponse webkit_soup_cache_has_response (WebKitSoupCache   *cache,
31
							SoupMessage *msg);
32
GInputStream *webkit_soup_cache_send_response (WebKitSoupCache   *cache,
33
					       SoupMessage *msg);
34
WebKitSoupCacheability  webkit_soup_cache_get_cacheability (WebKitSoupCache   *cache,
35
							    SoupMessage *msg);
36
SoupMessage *webkit_soup_cache_generate_conditional_request (WebKitSoupCache   *cache,
37
							     SoupMessage *original);
38
39
G_END_DECLS
40
41
#endif /* WEBKIT_SOUP_CACHE_PRIVATE_H */
- a/WebCore/platform/network/soup/cache/webkit/soup-cache.c +1652 lines
Line 0 a/WebCore/platform/network/soup/cache/webkit/soup-cache.c_sec1
1
/*
2
 * soup-cache.c
3
 *
4
 * Copyright (C) 2009, 2010 Igalia S.L.
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Library General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Library General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Library General Public License
17
 * along with this library; see the file COPYING.LIB.  If not, write to
18
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
 * Boston, MA 02110-1301, USA.
20
 */
21
22
/* TODO:
23
 * - Need to hook the feature in the sync SoupSession.
24
 * - Need more tests.
25
 */
26
27
#ifdef HAVE_CONFIG_H
28
#include <config.h>
29
#endif
30
31
#include "soup-cache.h"
32
#include "soup-cache-private.h"
33
#include <libsoup/soup.h>
34
#include <gio/gio.h>
35
#include <stdlib.h>
36
37
static SoupSessionFeatureInterface *webkit_soup_cache_default_feature_interface;
38
static void webkit_soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
39
40
#define DEFAULT_MAX_SIZE 50 * 1024 * 1024
41
#define MAX_ENTRY_DATA_PERCENTAGE 10 /* Percentage of the total size
42
	                                of the cache that can be
43
	                                filled by a single entry */
44
45
typedef struct _WebKitSoupCacheEntry {
46
	char *key;
47
	char *filename;
48
	guint freshness_lifetime;
49
	gboolean must_revalidate;
50
	GString *data;
51
	gsize pos;
52
	gsize length;
53
	time_t corrected_initial_age;
54
	time_t response_time;
55
	gboolean writing;
56
	gboolean dirty;
57
	gboolean got_body;
58
	gboolean being_validated;
59
	SoupMessageHeaders *headers;
60
	GOutputStream *stream;
61
	GError *error;
62
	guint hits;
63
	GCancellable *cancellable;
64
} WebKitSoupCacheEntry;
65
66
struct _WebKitSoupCachePrivate {
67
	char *cache_dir;
68
	GHashTable *cache;
69
	guint n_pending;
70
	SoupSession *session;
71
	WebKitSoupCacheType cache_type;
72
	guint size;
73
	guint max_size;
74
	guint max_entry_data_size; /* Computed value. Here for performance reasons */
75
	GList *lru_start;
76
};
77
78
typedef struct {
79
	WebKitSoupCache *cache;
80
	WebKitSoupCacheEntry *entry;
81
	SoupMessage *msg;
82
	gulong got_chunk_handler;
83
	gulong got_body_handler;
84
	gulong restarted_handler;
85
} WebKitSoupCacheWritingFixture;
86
87
enum {
88
	PROP_0,
89
	PROP_CACHE_DIR,
90
	PROP_CACHE_TYPE
91
};
92
93
#define WEBKIT_SOUP_CACHE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCachePrivate))
94
95
G_DEFINE_TYPE_WITH_CODE (WebKitSoupCache, webkit_soup_cache, G_TYPE_OBJECT,
96
			 G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
97
						webkit_soup_cache_session_feature_init))
98
99
static gboolean webkit_soup_cache_entry_remove (WebKitSoupCache *cache, WebKitSoupCacheEntry *entry);
100
static void make_room_for_new_entry (WebKitSoupCache *cache, guint length_to_add);
101
static gboolean cache_accepts_entries_of_size (WebKitSoupCache *cache, guint length_to_add);
102
103
static WebKitSoupCacheability
104
get_cacheability (WebKitSoupCache *cache, SoupMessage *msg)
105
{
106
	WebKitSoupCacheability cacheability;
107
	const char *cache_control;
108
109
	/* 1. The request method must be cacheable */
110
	if (msg->method == SOUP_METHOD_GET)
111
		cacheability = WEBKIT_SOUP_CACHE_CACHEABLE;
112
	else if (msg->method == SOUP_METHOD_HEAD ||
113
		 msg->method == SOUP_METHOD_TRACE ||
114
		 msg->method == SOUP_METHOD_CONNECT)
115
		return WEBKIT_SOUP_CACHE_UNCACHEABLE;
116
	else
117
		return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES);
118
119
	cache_control = soup_message_headers_get (msg->response_headers, "Cache-Control");
120
	if (cache_control) {
121
		GHashTable *hash;
122
		WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
123
124
		hash = soup_header_parse_param_list (cache_control);
125
126
		/* Shared caches MUST NOT store private resources */
127
		if (priv->cache_type == WEBKIT_SOUP_CACHE_SHARED) {
128
			if (g_hash_table_lookup_extended (hash, "private", NULL, NULL)) {
129
				soup_header_free_param_list (hash);
130
				return WEBKIT_SOUP_CACHE_UNCACHEABLE;
131
			}
132
		}
133
134
		/* 2. The 'no-store' cache directive does not appear in the
135
		 * headers
136
		 */
137
		if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) {
138
			soup_header_free_param_list (hash);
139
			return WEBKIT_SOUP_CACHE_UNCACHEABLE;
140
		}
141
142
		/* This does not appear in section 2.1, but I think it makes
143
		 * sense to check it too?
144
		 */
145
		if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) {
146
			soup_header_free_param_list (hash);
147
			return WEBKIT_SOUP_CACHE_UNCACHEABLE;
148
		}
149
	}
150
151
	switch (msg->status_code) {
152
		case SOUP_STATUS_PARTIAL_CONTENT:
153
			/* We don't cache partial responses, but they only
154
			 * invalidate cached full responses if the headers
155
			 * don't match.
156
			 */
157
			cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE;
158
			break;
159
160
		case SOUP_STATUS_NOT_MODIFIED:
161
			/* A 304 response validates an existing cache entry */
162
			cacheability = WEBKIT_SOUP_CACHE_VALIDATES;
163
			break;
164
165
		case SOUP_STATUS_MULTIPLE_CHOICES:
166
		case SOUP_STATUS_MOVED_PERMANENTLY:
167
		case SOUP_STATUS_GONE:
168
			/* FIXME: cacheable unless indicated otherwise */
169
			cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE;
170
			break;
171
172
		case SOUP_STATUS_FOUND:
173
		case SOUP_STATUS_TEMPORARY_REDIRECT:
174
			/* FIXME: cacheable if explicitly indicated */
175
			cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE;
176
			break;
177
178
		case SOUP_STATUS_SEE_OTHER:
179
		case SOUP_STATUS_FORBIDDEN:
180
		case SOUP_STATUS_NOT_FOUND:
181
		case SOUP_STATUS_METHOD_NOT_ALLOWED:
182
			return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES);
183
184
		default:
185
			/* Any 5xx status or any 4xx status not handled above
186
			 * is uncacheable but doesn't break the cache.
187
			 */
188
			if ((msg->status_code >= SOUP_STATUS_BAD_REQUEST &&
189
			     msg->status_code <= SOUP_STATUS_FAILED_DEPENDENCY) ||
190
			    msg->status_code >= SOUP_STATUS_INTERNAL_SERVER_ERROR)
191
				return WEBKIT_SOUP_CACHE_UNCACHEABLE;
192
193
			/* An unrecognized 2xx, 3xx, or 4xx response breaks
194
			 * the cache.
195
			 */
196
			if ((msg->status_code > SOUP_STATUS_PARTIAL_CONTENT &&
197
			     msg->status_code < SOUP_STATUS_MULTIPLE_CHOICES) ||
198
			    (msg->status_code > SOUP_STATUS_TEMPORARY_REDIRECT &&
199
			     msg->status_code < SOUP_STATUS_INTERNAL_SERVER_ERROR))
200
				return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES);
201
			break;
202
	}
203
204
	return cacheability;
205
}
206
207
static void
208
webkit_soup_cache_entry_free (WebKitSoupCacheEntry *entry, gboolean purge)
209
{
210
	if (purge) {
211
		GFile *file = g_file_new_for_path (entry->filename);
212
		g_file_delete (file, NULL, NULL);
213
		g_object_unref (file);
214
	}
215
216
	g_free (entry->filename);
217
	entry->filename = NULL;
218
	g_free (entry->key);
219
	entry->key = NULL;
220
221
	if (entry->headers) {
222
		soup_message_headers_free (entry->headers);
223
		entry->headers = NULL;
224
	}
225
226
	if (entry->data) {
227
		g_string_free (entry->data, TRUE);
228
		entry->data = NULL;
229
	}
230
	if (entry->error) {
231
		g_error_free (entry->error);
232
		entry->error = NULL;
233
	}
234
	if (entry->cancellable) {
235
		g_object_unref (entry->cancellable);
236
		entry->cancellable = NULL;
237
	}
238
239
	g_slice_free (WebKitSoupCacheEntry, entry);
240
}
241
242
static void
243
copy_headers (const char *name, const char *value, SoupMessageHeaders *headers)
244
{
245
	soup_message_headers_append (headers, name, value);
246
}
247
248
static void
249
update_headers (const char *name, const char *value, SoupMessageHeaders *headers)
250
{
251
	if (soup_message_headers_get (headers, name))
252
		soup_message_headers_replace (headers, name, value);
253
	else
254
		soup_message_headers_append (headers, name, value);
255
}
256
257
static guint
258
webkit_soup_cache_entry_get_current_age (WebKitSoupCacheEntry *entry)
259
{
260
	time_t now = time (NULL);
261
	time_t resident_time;
262
263
	resident_time = now - entry->response_time;
264
	return entry->corrected_initial_age + resident_time;
265
}
266
267
static gboolean
268
webkit_soup_cache_entry_is_fresh_enough (WebKitSoupCacheEntry *entry, int min_fresh)
269
{
270
	unsigned limit = (min_fresh == -1) ? webkit_soup_cache_entry_get_current_age (entry) : min_fresh;
271
	return entry->freshness_lifetime > limit;
272
}
273
274
static char *
275
soup_message_get_cache_key (SoupMessage *msg)
276
{
277
	SoupURI *uri = soup_message_get_uri (msg);
278
	return soup_uri_to_string (uri, FALSE);
279
}
280
281
static void
282
webkit_soup_cache_entry_set_freshness (WebKitSoupCacheEntry *entry, SoupMessage *msg, WebKitSoupCache *cache)
283
{
284
	const char *cache_control;
285
	const char *expires, *date, *last_modified;
286
	GHashTable *hash;
287
288
	hash = NULL;
289
290
	cache_control = soup_message_headers_get (entry->headers, "Cache-Control");
291
	if (cache_control) {
292
		const char *max_age, *s_maxage;
293
		gint64 freshness_lifetime = 0;
294
		WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
295
296
		hash = soup_header_parse_param_list (cache_control);
297
298
		/* Should we re-validate the entry when it goes stale */
299
		entry->must_revalidate = (gboolean)g_hash_table_lookup (hash, "must-revalidate");
300
301
		/* Section 2.3.1 */
302
		if (priv->cache_type == WEBKIT_SOUP_CACHE_SHARED) {
303
			s_maxage = g_hash_table_lookup (hash, "s-maxage");
304
			if (s_maxage) {
305
				freshness_lifetime = g_ascii_strtoll (s_maxage, NULL, 10);
306
				if (freshness_lifetime) {
307
					/* Implies proxy-revalidate. TODO: is it true? */
308
					entry->must_revalidate = TRUE;
309
					soup_header_free_param_list (hash);
310
					return;
311
				}
312
			}
313
		}
314
315
		/* If 'max-age' cache directive is present, use that */
316
		max_age = g_hash_table_lookup (hash, "max-age");
317
		if (max_age)
318
			freshness_lifetime = g_ascii_strtoll (max_age, NULL, 10);
319
320
		if (freshness_lifetime) {
321
			entry->freshness_lifetime = (guint)MIN (freshness_lifetime, G_MAXUINT32);
322
			soup_header_free_param_list (hash);
323
			return;
324
		}
325
	}
326
327
	if (hash != NULL)
328
		soup_header_free_param_list (hash);
329
330
	/* If the 'Expires' response header is present, use its value
331
	 * minus the value of the 'Date' response header
332
	 */
333
	expires = soup_message_headers_get (entry->headers, "Expires");
334
	date = soup_message_headers_get (entry->headers, "Date");
335
	if (expires && date) {
336
		SoupDate *expires_d, *date_d;
337
		time_t expires_t, date_t;
338
339
		expires_d = soup_date_new_from_string (expires);
340
		if (expires_d) {
341
			date_d = soup_date_new_from_string (date);
342
343
			expires_t = soup_date_to_time_t (expires_d);
344
			date_t = soup_date_to_time_t (date_d);
345
346
			soup_date_free (expires_d);
347
			soup_date_free (date_d);
348
349
			if (expires_t && date_t) {
350
				entry->freshness_lifetime = (guint)MAX (expires_t - date_t, 0);
351
				return;
352
			}
353
		} else {
354
			/* If Expires is not a valid date we should
355
			   treat it as already expired, see section
356
			   3.3 */
357
			entry->freshness_lifetime = 0;
358
			return;
359
		}
360
	}
361
362
	/* Otherwise an heuristic may be used */
363
364
	/* Heuristics MUST NOT be used with these status codes
365
	   (section 2.3.1.1) */
366
	if (msg->status_code != SOUP_STATUS_OK &&
367
	    msg->status_code != SOUP_STATUS_NON_AUTHORITATIVE &&
368
	    msg->status_code != SOUP_STATUS_PARTIAL_CONTENT &&
369
	    msg->status_code != SOUP_STATUS_MULTIPLE_CHOICES &&
370
	    msg->status_code != SOUP_STATUS_MOVED_PERMANENTLY &&
371
	    msg->status_code != SOUP_STATUS_GONE)
372
		goto expire;
373
374
	/* TODO: attach warning 113 if response's current_age is more
375
	   than 24h (section 2.3.1.1) when using heuristics */
376
377
	/* Last-Modified based heuristic */
378
	last_modified = soup_message_headers_get (entry->headers, "Last-Modified");
379
	if (last_modified) {
380
		SoupDate *soup_date;
381
		time_t now, last_modified_t;
382
383
		soup_date = soup_date_new_from_string (last_modified);
384
		last_modified_t = soup_date_to_time_t (soup_date);
385
		now = time (NULL);
386
387
#define HEURISTIC_FACTOR 0.1 /* From Section 2.3.1.1 */
388
389
		entry->freshness_lifetime = MAX (0, (now - last_modified_t) * HEURISTIC_FACTOR);
390
		soup_date_free (soup_date);
391
	}
392
393
	return;
394
395
 expire:
396
	/* If all else fails, make the entry expire immediately */
397
	entry->freshness_lifetime = 0;
398
}
399
400
static WebKitSoupCacheEntry *
401
webkit_soup_cache_entry_new (WebKitSoupCache *cache, SoupMessage *msg, time_t request_time, time_t response_time)
402
{
403
	WebKitSoupCacheEntry *entry;
404
	SoupMessageHeaders *headers;
405
	const char *date;
406
	char *md5;
407
408
	entry = g_slice_new0 (WebKitSoupCacheEntry);
409
	entry->dirty = FALSE;
410
	entry->writing = FALSE;
411
	entry->got_body = FALSE;
412
	entry->being_validated = FALSE;
413
	entry->data = g_string_new (NULL);
414
	entry->pos = 0;
415
	entry->error = NULL;
416
417
	/* key & filename */
418
	entry->key = soup_message_get_cache_key (msg);
419
	md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, entry->key, -1);
420
	entry->filename = g_build_filename (cache->priv->cache_dir, md5, NULL);
421
	g_free (md5);
422
423
	/* Headers */
424
	headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
425
	soup_message_headers_foreach (msg->response_headers,
426
				      (SoupMessageHeadersForeachFunc)copy_headers,
427
				      headers);
428
	entry->headers = headers;
429
430
	/* LRU list */
431
	entry->hits = 0;
432
433
	/* Section 2.3.1, Freshness Lifetime */
434
	webkit_soup_cache_entry_set_freshness (entry, msg, cache);
435
436
	/* Section 2.3.2, Calculating Age */
437
	date = soup_message_headers_get (entry->headers, "Date");
438
439
	if (date) {
440
		SoupDate *soup_date;
441
		const char *age;
442
		time_t date_value, apparent_age, corrected_received_age, response_delay, age_value = 0;
443
444
		soup_date = soup_date_new_from_string (date);
445
		date_value = soup_date_to_time_t (soup_date);
446
		soup_date_free (soup_date);
447
448
		age = soup_message_headers_get (entry->headers, "Age");
449
		if (age)
450
			age_value = g_ascii_strtoll (age, NULL, 10);
451
452
		entry->response_time = response_time;
453
		apparent_age = MAX (0, entry->response_time - date_value);
454
		corrected_received_age = MAX (apparent_age, age_value);
455
		response_delay = entry->response_time - request_time;
456
		entry->corrected_initial_age = corrected_received_age + response_delay;
457
	} else {
458
		/* Is this correct ? */
459
		entry->corrected_initial_age = time (NULL);
460
	}
461
462
	return entry;
463
}
464
465
static void
466
webkit_soup_cache_writing_fixture_free (WebKitSoupCacheWritingFixture *fixture)
467
{
468
	/* Free fixture. And disconnect signals, we don't want to
469
	   listen to more SoupMessage events as we're finished with
470
	   this resource */
471
	if (g_signal_handler_is_connected (fixture->msg, fixture->got_chunk_handler))
472
		g_signal_handler_disconnect (fixture->msg, fixture->got_chunk_handler);
473
	if (g_signal_handler_is_connected (fixture->msg, fixture->got_body_handler))
474
		g_signal_handler_disconnect (fixture->msg, fixture->got_body_handler);
475
	if (g_signal_handler_is_connected (fixture->msg, fixture->restarted_handler))
476
		g_signal_handler_disconnect (fixture->msg, fixture->restarted_handler);
477
	g_object_unref (fixture->msg);
478
	g_object_unref (fixture->cache);
479
	g_slice_free (WebKitSoupCacheWritingFixture, fixture);
480
}
481
482
static void
483
close_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture)
484
{
485
	WebKitSoupCacheEntry *entry = fixture->entry;
486
	WebKitSoupCache *cache = fixture->cache;
487
	GOutputStream *stream = G_OUTPUT_STREAM (source);
488
	goffset content_length;
489
490
	g_warn_if_fail (entry->error == NULL);
491
492
	/* FIXME: what do we do on error ? */
493
494
	if (stream) {
495
		g_output_stream_close_finish (stream, result, NULL);
496
		g_object_unref (stream);
497
	}
498
	entry->stream = NULL;
499
500
	content_length = soup_message_headers_get_content_length (entry->headers);
501
502
	/* If the process was cancelled, then delete the entry from
503
	   the cache. Do it also if the size of a chunked resource is
504
	   too much for the cache */
505
	if (g_cancellable_is_cancelled (entry->cancellable)) {
506
		entry->dirty = FALSE;
507
		webkit_soup_cache_entry_remove (cache, entry);
508
		webkit_soup_cache_entry_free (entry, TRUE);
509
		entry = NULL;
510
	} else if ((soup_message_headers_get_encoding (entry->headers) == SOUP_ENCODING_CHUNKED) ||
511
		   entry->length != content_length) {
512
		/** Two options here:
513
		 *
514
		 * 1. "chunked" data, entry was temporarily added to
515
		 * cache (as content-length is 0) and now that we have
516
		 * the actual size we have to evaluate if we want it
517
		 * in the cache or not
518
		 *
519
		 * 2. Content-Length has a different value than actual
520
		 * length, means that the content was encoded for
521
		 * transmission (typically compressed) and thus we
522
		 * have to substract the content-length value that was
523
		 * added to the cache and add the unencoded length
524
		 **/
525
		gint length_to_add = entry->length - content_length;
526
527
		/* Make room in cache if needed */
528
		if (cache_accepts_entries_of_size (cache, length_to_add)) {
529
			make_room_for_new_entry (cache, length_to_add);
530
531
			cache->priv->size += length_to_add;
532
		} else {
533
			entry->dirty = FALSE;
534
			webkit_soup_cache_entry_remove (cache, entry);
535
			webkit_soup_cache_entry_free (entry, TRUE);
536
			entry = NULL;
537
		}
538
	}
539
540
	if (entry) {
541
		/* Get rid of the GString in memory for the resource now */
542
		if (entry->data) {
543
			g_string_free (entry->data, TRUE);
544
			entry->data = NULL;
545
		}
546
547
		entry->dirty = FALSE;
548
		entry->writing = FALSE;
549
		entry->got_body = FALSE;
550
		entry->pos = 0;
551
552
		g_object_unref (entry->cancellable);
553
		entry->cancellable = NULL;
554
	}
555
556
	cache->priv->n_pending--;
557
558
	/* Frees */
559
	webkit_soup_cache_writing_fixture_free (fixture);
560
}
561
562
static void
563
write_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture)
564
{
565
	GOutputStream *stream = G_OUTPUT_STREAM (source);
566
	GError *error = NULL;
567
	gssize write_size;
568
	WebKitSoupCacheEntry *entry = fixture->entry;
569
570
	if (g_cancellable_is_cancelled (entry->cancellable)) {
571
		g_output_stream_close_async (stream,
572
					     G_PRIORITY_LOW,
573
					     entry->cancellable,
574
					     (GAsyncReadyCallback)close_ready_cb,
575
					     fixture);
576
		return;
577
	}
578
579
	write_size = g_output_stream_write_finish (stream, result, &error);
580
	if (write_size <= 0 || error) {
581
		if (error)
582
			entry->error = error;
583
		g_output_stream_close_async (stream,
584
					     G_PRIORITY_LOW,
585
					     entry->cancellable,
586
					     (GAsyncReadyCallback)close_ready_cb,
587
					     fixture);
588
		/* FIXME: We should completely stop caching the
589
		   resource at this point */
590
	} else {
591
		entry->pos += write_size;
592
593
		/* Are we still writing and is there new data to write
594
		   already ? */
595
		if (entry->data && entry->pos < entry->data->len) {
596
			g_output_stream_write_async (entry->stream,
597
						     entry->data->str + entry->pos,
598
						     entry->data->len - entry->pos,
599
						     G_PRIORITY_LOW,
600
						     entry->cancellable,
601
						     (GAsyncReadyCallback)write_ready_cb,
602
						     fixture);
603
		} else {
604
			entry->writing = FALSE;
605
606
			if (entry->got_body) {
607
				/* If we already received 'got-body'
608
				   and we have written all the data,
609
				   we can close the stream */
610
				g_output_stream_close_async (entry->stream,
611
							     G_PRIORITY_LOW,
612
							     entry->cancellable,
613
							     (GAsyncReadyCallback)close_ready_cb,
614
							     fixture);
615
			}
616
		}
617
	}
618
}
619
620
static void
621
msg_got_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, WebKitSoupCacheWritingFixture *fixture)
622
{
623
	WebKitSoupCacheEntry *entry = fixture->entry;
624
625
	g_return_if_fail (chunk->data && chunk->length);
626
	g_return_if_fail (entry);
627
628
	/* Ignore this if the writing or appending was cancelled */
629
	if (!g_cancellable_is_cancelled (entry->cancellable)) {
630
		g_string_append_len (entry->data, chunk->data, chunk->length);
631
		entry->length = entry->data->len;
632
633
		if (!cache_accepts_entries_of_size (fixture->cache, entry->length)) {
634
			/* Quickly cancel the caching of the resource */
635
			g_cancellable_cancel (entry->cancellable);
636
		}
637
	}
638
639
	/* FIXME: remove the error check when we cancel the caching at
640
	   the first write error */
641
	/* Only write if the entry stream is ready */
642
	if (entry->writing == FALSE && entry->error == NULL && entry->stream) {
643
		GString *data = entry->data;
644
		entry->writing = TRUE;
645
		g_output_stream_write_async (entry->stream,
646
					     data->str + entry->pos,
647
					     data->len - entry->pos,
648
					     G_PRIORITY_LOW,
649
					     entry->cancellable,
650
					     (GAsyncReadyCallback)write_ready_cb,
651
					     fixture);
652
	}
653
}
654
655
static void
656
msg_got_body_cb (SoupMessage *msg, WebKitSoupCacheWritingFixture *fixture)
657
{
658
	WebKitSoupCacheEntry *entry = fixture->entry;
659
	g_return_if_fail (entry);
660
661
	entry->got_body = TRUE;
662
663
	if (!entry->stream && entry->pos != entry->length)
664
		/* The stream is not ready to be written but we still
665
		   have data to write, we'll write it when the stream
666
		   is opened for writing */
667
		return;
668
669
670
	if (entry->pos != entry->length) {
671
		/* If we still have data to write, write it,
672
		   write_ready_cb will close the stream */
673
		if (entry->writing == FALSE && entry->error == NULL && entry->stream) {
674
			g_output_stream_write_async (entry->stream,
675
						     entry->data->str + entry->pos,
676
						     entry->data->len - entry->pos,
677
						     G_PRIORITY_LOW,
678
						     entry->cancellable,
679
						     (GAsyncReadyCallback)write_ready_cb,
680
						     fixture);
681
		}
682
		return;
683
	}
684
685
	if (entry->stream && !entry->writing)
686
		g_output_stream_close_async (entry->stream,
687
					     G_PRIORITY_LOW,
688
					     entry->cancellable,
689
					     (GAsyncReadyCallback)close_ready_cb,
690
					     fixture);
691
}
692
693
static gboolean
694
webkit_soup_cache_entry_remove (WebKitSoupCache *cache, WebKitSoupCacheEntry *entry)
695
{
696
	GList *lru_item;
697
698
	/* if (entry->dirty && !g_cancellable_is_cancelled (entry->cancellable)) { */
699
	if (entry->dirty) {
700
		g_cancellable_cancel (entry->cancellable);
701
		return FALSE;
702
	}
703
704
	g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache));
705
706
	/* Remove from cache */
707
	if (!g_hash_table_remove (cache->priv->cache, entry->key))
708
		return FALSE;
709
710
	/* Remove from LRU */
711
	lru_item = g_list_find (cache->priv->lru_start, entry);
712
	cache->priv->lru_start = g_list_delete_link (cache->priv->lru_start, lru_item);
713
714
	/* Adjust cache size */
715
	cache->priv->size -= entry->length;
716
717
	g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache));
718
719
	return TRUE;
720
}
721
722
static gint
723
lru_compare_func (gconstpointer a, gconstpointer b)
724
{
725
	WebKitSoupCacheEntry *entry_a = (WebKitSoupCacheEntry *)a;
726
	WebKitSoupCacheEntry *entry_b = (WebKitSoupCacheEntry *)b;
727
728
	/** The rationale of this sorting func is
729
	 *
730
	 * 1. sort by hits -> LRU algorithm, then
731
	 *
732
	 * 2. sort by freshness lifetime, we better discard first
733
	 * entries that are close to expire
734
	 *
735
	 * 3. sort by size, replace first small size resources as they
736
	 * are cheaper to download
737
	 **/
738
739
	/* Sort by hits */
740
	if (entry_a->hits != entry_b->hits)
741
		return entry_a->hits - entry_b->hits;
742
743
	/* Sort by freshness_lifetime */
744
	if (entry_a->freshness_lifetime != entry_b->freshness_lifetime)
745
		return entry_a->freshness_lifetime - entry_b->freshness_lifetime;
746
747
	/* Sort by size */
748
	return entry_a->length - entry_b->length;
749
}
750
751
static gboolean
752
cache_accepts_entries_of_size (WebKitSoupCache *cache, guint length_to_add)
753
{
754
	/* We could add here some more heuristics. TODO: review how
755
	   this is done by other HTTP caches */
756
757
	return length_to_add <= cache->priv->max_entry_data_size;
758
}
759
760
static void
761
make_room_for_new_entry (WebKitSoupCache *cache, guint length_to_add)
762
{
763
	GList *lru_entry = cache->priv->lru_start;
764
765
	/* Check that there is enough room for the new entry. This is
766
	   an approximation as we're not working out the size of the
767
	   cache file or the size of the headers for performance
768
	   reasons. TODO: check if that would be really that expensive */
769
770
	while (lru_entry &&
771
	       (length_to_add + cache->priv->size > cache->priv->max_size)) {
772
		WebKitSoupCacheEntry *old_entry = (WebKitSoupCacheEntry *)lru_entry->data;
773
774
		/* Discard entries. Once cancelled resources will be
775
		 * freed in close_ready_cb
776
		 */
777
		if (webkit_soup_cache_entry_remove (cache, old_entry)) {
778
			webkit_soup_cache_entry_free (old_entry, TRUE);
779
			lru_entry = cache->priv->lru_start;
780
		} else
781
			lru_entry = g_list_next (lru_entry);
782
	}
783
}
784
785
static gboolean
786
webkit_soup_cache_entry_insert_by_key (WebKitSoupCache *cache,
787
				       const char *key,
788
				       WebKitSoupCacheEntry *entry,
789
				       gboolean sort)
790
{
791
	guint length_to_add = 0;
792
793
	if (soup_message_headers_get_encoding (entry->headers) != SOUP_ENCODING_CHUNKED)
794
		length_to_add = soup_message_headers_get_content_length (entry->headers);
795
796
	/* Check if we are going to store the resource depending on its size */
797
	if (length_to_add) {
798
		if (!cache_accepts_entries_of_size (cache, length_to_add))
799
			return FALSE;
800
801
		/* Make room for new entry if needed */
802
		make_room_for_new_entry (cache, length_to_add);
803
	}
804
805
	g_hash_table_insert (cache->priv->cache, g_strdup (key), entry);
806
807
	/* Compute new cache size */
808
	cache->priv->size += length_to_add;
809
810
	/* Update LRU */
811
	if (sort)
812
		cache->priv->lru_start = g_list_insert_sorted (cache->priv->lru_start, entry, lru_compare_func);
813
	else
814
		cache->priv->lru_start = g_list_prepend (cache->priv->lru_start, entry);
815
816
	g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache));
817
818
	return TRUE;
819
}
820
821
static void
822
msg_restarted_cb (SoupMessage *msg, WebKitSoupCacheEntry *entry)
823
{
824
	/* FIXME: What should we do here exactly? */
825
}
826
827
static void
828
append_to_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture)
829
{
830
	GFile *file = (GFile *)source;
831
	GOutputStream *stream;
832
	WebKitSoupCacheEntry *entry = fixture->entry;
833
834
	stream = (GOutputStream *)g_file_append_to_finish (file, result, &entry->error);
835
836
	if (g_cancellable_is_cancelled (entry->cancellable) || entry->error) {
837
		fixture->cache->priv->n_pending--;
838
		entry->dirty = FALSE;
839
		webkit_soup_cache_entry_remove (fixture->cache, entry);
840
		webkit_soup_cache_entry_free (entry, TRUE);
841
		webkit_soup_cache_writing_fixture_free (fixture);
842
		return;
843
	}
844
845
	entry->stream = g_object_ref (stream);
846
	g_object_unref (file);
847
848
	/* If we already got all the data we have to initiate the
849
	   writing here, since we won't get more 'got-chunk'
850
	   signals */
851
	if (entry->got_body) {
852
		GString *data = entry->data;
853
854
		/* It could happen that reading the data from server
855
		   was completed before this happens. In that case
856
		   there is no data */
857
		if (data) {
858
			entry->writing = TRUE;
859
			g_output_stream_write_async (entry->stream,
860
						     data->str + entry->pos,
861
						     data->len - entry->pos,
862
						     G_PRIORITY_LOW,
863
						     entry->cancellable,
864
						     (GAsyncReadyCallback)write_ready_cb,
865
						     fixture);
866
		}
867
	}
868
}
869
870
typedef struct {
871
	time_t request_time;
872
	SoupSessionFeature *feature;
873
	gulong got_headers_handler;
874
} RequestHelper;
875
876
static void
877
msg_got_headers_cb (SoupMessage *msg, gpointer user_data)
878
{
879
	WebKitSoupCache *cache;
880
	WebKitSoupCacheability cacheable;
881
	RequestHelper *helper;
882
	time_t request_time, response_time;
883
884
	response_time = time (NULL);
885
886
	helper = (RequestHelper *)user_data;
887
	cache = WEBKIT_SOUP_CACHE (helper->feature);
888
	request_time = helper->request_time;
889
	g_signal_handlers_disconnect_by_func (msg, msg_got_headers_cb, user_data);
890
	g_slice_free (RequestHelper, helper);
891
892
	cacheable = webkit_soup_cache_get_cacheability (cache, msg);
893
894
	if (cacheable & WEBKIT_SOUP_CACHE_CACHEABLE) {
895
		WebKitSoupCacheEntry *entry;
896
		char *key;
897
		GFile *file;
898
		WebKitSoupCacheWritingFixture *fixture;
899
900
		/* Check if we are already caching this resource */
901
		key = soup_message_get_cache_key (msg);
902
		entry = g_hash_table_lookup (cache->priv->cache, key);
903
		g_free (key);
904
905
		if (entry && entry->dirty)
906
			return;
907
908
		/* Create a new entry, deleting any old one if present */
909
		if (entry) {
910
			webkit_soup_cache_entry_remove (cache, entry);
911
			webkit_soup_cache_entry_free (entry, TRUE);
912
		}
913
914
		entry = webkit_soup_cache_entry_new (cache, msg, request_time, response_time);
915
		entry->hits = 1;
916
917
		/* Do not continue if it can not be stored */
918
		if (!webkit_soup_cache_entry_insert_by_key (cache, (const gchar *)entry->key, entry, TRUE)) {
919
			webkit_soup_cache_entry_free (entry, TRUE);
920
			return;
921
		}
922
923
		fixture = g_slice_new0 (WebKitSoupCacheWritingFixture);
924
		fixture->cache = g_object_ref (cache);
925
		fixture->entry = entry;
926
		fixture->msg = g_object_ref (msg);
927
928
		/* We connect now to these signals and buffer the data
929
		   if it comes before the file is ready for writing */
930
		fixture->got_chunk_handler =
931
			g_signal_connect (msg, "got-chunk", G_CALLBACK (msg_got_chunk_cb), fixture);
932
		fixture->got_body_handler =
933
			g_signal_connect (msg, "got-body", G_CALLBACK (msg_got_body_cb), fixture);
934
		fixture->restarted_handler =
935
			g_signal_connect (msg, "restarted", G_CALLBACK (msg_restarted_cb), entry);
936
937
		/* Prepare entry */
938
		file = g_file_new_for_path (entry->filename);
939
		cache->priv->n_pending++;
940
941
		entry->dirty = TRUE;
942
		entry->cancellable = g_cancellable_new ();
943
		g_file_append_to_async (file, 0,
944
					G_PRIORITY_LOW, entry->cancellable,
945
					(GAsyncReadyCallback)append_to_ready_cb,
946
					fixture);
947
	} else if (cacheable & WEBKIT_SOUP_CACHE_INVALIDATES) {
948
		char *key;
949
		WebKitSoupCacheEntry *entry;
950
951
		key = soup_message_get_cache_key (msg);
952
		entry = g_hash_table_lookup (cache->priv->cache, key);
953
		g_free (key);
954
955
		if (entry) {
956
			if (webkit_soup_cache_entry_remove (cache, entry))
957
				webkit_soup_cache_entry_free (entry, TRUE);
958
		}
959
	} else if (cacheable & WEBKIT_SOUP_CACHE_VALIDATES) {
960
		char *key;
961
		WebKitSoupCacheEntry *entry;
962
963
		key = soup_message_get_cache_key (msg);
964
		entry = g_hash_table_lookup (cache->priv->cache, key);
965
		g_free (key);
966
967
		g_return_if_fail (entry);
968
969
		entry->being_validated = FALSE;
970
971
		/* We update the headers of the existing cache item,
972
		   plus its age */
973
		soup_message_headers_foreach (msg->response_headers,
974
					      (SoupMessageHeadersForeachFunc)update_headers,
975
					      entry->headers);
976
		webkit_soup_cache_entry_set_freshness (entry, msg, cache);
977
	}
978
}
979
980
GInputStream *
981
webkit_soup_cache_send_response (WebKitSoupCache *cache, SoupMessage *msg)
982
{
983
	char *key;
984
	WebKitSoupCacheEntry *entry;
985
	char *current_age;
986
	GInputStream *stream = NULL;
987
	GFile *file;
988
989
	g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), NULL);
990
	g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
991
992
	key = soup_message_get_cache_key (msg);
993
	entry = g_hash_table_lookup (cache->priv->cache, key);
994
	g_return_val_if_fail (entry, NULL);
995
996
	/* If we are told to send a response from cache any validation
997
	   in course is over by now */
998
	entry->being_validated = FALSE;
999
1000
	/* Headers */
1001
	soup_message_headers_foreach (entry->headers,
1002
				      (SoupMessageHeadersForeachFunc)update_headers,
1003
				      msg->response_headers);
1004
1005
	/* Add 'Age' header with the current age */
1006
	current_age = g_strdup_printf ("%d", webkit_soup_cache_entry_get_current_age (entry));
1007
	soup_message_headers_replace (msg->response_headers,
1008
				      "Age",
1009
				      current_age);
1010
	g_free (current_age);
1011
1012
	/* TODO: the original idea was to save reads, but current code
1013
	   assumes that a stream is always returned. Need to reach
1014
	   some agreement here. Also we have to handle the situation
1015
	   were the file was no longer there (for example files
1016
	   removed without notifying the cache */
1017
	file = g_file_new_for_path (entry->filename);
1018
	stream = (GInputStream *)g_file_read (file, NULL, NULL);
1019
1020
	return stream;
1021
}
1022
1023
static void
1024
request_started (SoupSessionFeature *feature, SoupSession *session,
1025
		 SoupMessage *msg, SoupSocket *socket)
1026
{
1027
	RequestHelper *helper = g_slice_new0 (RequestHelper);
1028
	helper->request_time = time (NULL);
1029
	helper->feature = feature;
1030
	helper->got_headers_handler = g_signal_connect (msg, "got-headers",
1031
							G_CALLBACK (msg_got_headers_cb),
1032
							helper);
1033
}
1034
1035
static void
1036
attach (SoupSessionFeature *feature, SoupSession *session)
1037
{
1038
	WebKitSoupCache *cache = WEBKIT_SOUP_CACHE (feature);
1039
	cache->priv->session = session;
1040
1041
	webkit_soup_cache_default_feature_interface->attach (feature, session);
1042
}
1043
1044
static void
1045
webkit_soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface,
1046
					gpointer interface_data)
1047
{
1048
	webkit_soup_cache_default_feature_interface =
1049
		g_type_default_interface_peek (SOUP_TYPE_SESSION_FEATURE);
1050
1051
	feature_interface->attach = attach;
1052
	feature_interface->request_started = request_started;
1053
}
1054
1055
static void
1056
webkit_soup_cache_init (WebKitSoupCache *cache)
1057
{
1058
	WebKitSoupCachePrivate *priv;
1059
1060
	priv = cache->priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
1061
1062
	priv->cache = g_hash_table_new_full (g_str_hash,
1063
					     g_str_equal,
1064
					     (GDestroyNotify)g_free,
1065
					     NULL);
1066
1067
	/* LRU */
1068
	priv->lru_start = NULL;
1069
1070
	/* */
1071
	priv->n_pending = 0;
1072
1073
	/* Cache size */
1074
	priv->max_size = DEFAULT_MAX_SIZE;
1075
	priv->max_entry_data_size = priv->max_size / MAX_ENTRY_DATA_PERCENTAGE;
1076
	priv->size = 0;
1077
}
1078
1079
static void
1080
remove_cache_item (gpointer key,
1081
		   gpointer value,
1082
		   WebKitSoupCache *cache)
1083
{
1084
	WebKitSoupCacheEntry *entry = g_hash_table_lookup (cache->priv->cache, (const gchar *)key);
1085
	if (webkit_soup_cache_entry_remove (cache, entry))
1086
		webkit_soup_cache_entry_free (entry, FALSE);
1087
}
1088
1089
static void
1090
webkit_soup_cache_finalize (GObject *object)
1091
{
1092
	WebKitSoupCachePrivate *priv;
1093
1094
	priv = WEBKIT_SOUP_CACHE (object)->priv;
1095
1096
	g_hash_table_foreach (priv->cache, (GHFunc)remove_cache_item, object);
1097
	g_hash_table_destroy (priv->cache);
1098
	g_free (priv->cache_dir);
1099
1100
	g_list_free (priv->lru_start);
1101
	priv->lru_start = NULL;
1102
1103
	G_OBJECT_CLASS (webkit_soup_cache_parent_class)->finalize (object);
1104
}
1105
1106
static void
1107
webkit_soup_cache_set_property (GObject *object, guint prop_id,
1108
				const GValue *value, GParamSpec *pspec)
1109
{
1110
	WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE (object)->priv;
1111
1112
	switch (prop_id) {
1113
		case PROP_CACHE_DIR:
1114
			priv->cache_dir = g_value_dup_string (value);
1115
			/* Create directory if it does not exist (FIXME: should we?) */
1116
			if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
1117
				g_mkdir_with_parents (priv->cache_dir, 0700);
1118
			break;
1119
		case PROP_CACHE_TYPE:
1120
			priv->cache_type = g_value_get_enum (value);
1121
			/* TODO: clear private entries and issue a warning if moving to shared? */
1122
			break;
1123
		default:
1124
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1125
			break;
1126
	}
1127
}
1128
1129
static void
1130
webkit_soup_cache_get_property (GObject *object, guint prop_id,
1131
				GValue *value, GParamSpec *pspec)
1132
{
1133
	WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE (object)->priv;
1134
1135
	switch (prop_id) {
1136
		case PROP_CACHE_DIR:
1137
			g_value_set_string (value, priv->cache_dir);
1138
			break;
1139
		case PROP_CACHE_TYPE:
1140
			g_value_set_enum (value, priv->cache_type);
1141
			break;
1142
		default:
1143
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1144
			break;
1145
	}
1146
}
1147
1148
static void
1149
webkit_soup_cache_constructed (GObject *object)
1150
{
1151
	WebKitSoupCachePrivate *priv;
1152
1153
	priv = WEBKIT_SOUP_CACHE (object)->priv;
1154
1155
	if (!priv->cache_dir) {
1156
		/* Set a default cache dir, different for each user */
1157
		priv->cache_dir = g_build_filename (g_get_user_cache_dir (),
1158
						    "httpcache",
1159
						    NULL);
1160
		if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
1161
			g_mkdir_with_parents (priv->cache_dir, 0700);
1162
	}
1163
1164
	if (G_OBJECT_CLASS (webkit_soup_cache_parent_class)->constructed)
1165
		G_OBJECT_CLASS (webkit_soup_cache_parent_class)->constructed (object);
1166
}
1167
1168
#define WEBKIT_SOUP_CACHE_TYPE_TYPE (webkit_soup_cache_type_get_type ())
1169
static GType
1170
webkit_soup_cache_type_get_type (void)
1171
{
1172
	static GType cache_type = 0;
1173
1174
	static const GEnumValue cache_types[] = {
1175
		{ WEBKIT_SOUP_CACHE_SINGLE_USER, "Single user cache", "user" },
1176
		{ WEBKIT_SOUP_CACHE_SHARED, "Shared cache", "shared" },
1177
		{ 0, NULL, NULL }
1178
	};
1179
1180
	if (!cache_type) {
1181
		cache_type = g_enum_register_static ("WebKitSoupCacheType", cache_types);
1182
	}
1183
	return cache_type;
1184
}
1185
1186
static void
1187
webkit_soup_cache_class_init (WebKitSoupCacheClass *cache_class)
1188
{
1189
	GObjectClass *gobject_class = (GObjectClass *)cache_class;
1190
1191
	gobject_class->finalize = webkit_soup_cache_finalize;
1192
	gobject_class->constructed = webkit_soup_cache_constructed;
1193
	gobject_class->set_property = webkit_soup_cache_set_property;
1194
	gobject_class->get_property = webkit_soup_cache_get_property;
1195
1196
	cache_class->get_cacheability = get_cacheability;
1197
1198
	g_object_class_install_property (gobject_class, PROP_CACHE_DIR,
1199
					 g_param_spec_string ("cache-dir",
1200
							      "Cache directory",
1201
							      "The directory to store the cache files",
1202
							      NULL,
1203
							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1204
1205
	g_object_class_install_property (gobject_class, PROP_CACHE_TYPE,
1206
					 g_param_spec_enum ("cache-type",
1207
							    "Cache type",
1208
							    "Whether the cache is private or shared",
1209
							    WEBKIT_SOUP_CACHE_TYPE_TYPE,
1210
							    WEBKIT_SOUP_CACHE_SINGLE_USER,
1211
							    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1212
1213
	g_type_class_add_private (cache_class, sizeof (WebKitSoupCachePrivate));
1214
}
1215
1216
/**
1217
 * webkit_soup_cache_new:
1218
 * @cache_dir: the directory to store the cached data, or %NULL to use the default one
1219
 * @cache_type: the #WebKitSoupCacheType of the cache
1220
 *
1221
 * Creates a new #WebKitSoupCache.
1222
 *
1223
 * Returns: a new #WebKitSoupCache
1224
 *
1225
 * Since: 2.28
1226
 **/
1227
WebKitSoupCache *
1228
webkit_soup_cache_new (const char *cache_dir, WebKitSoupCacheType cache_type)
1229
{
1230
	return g_object_new (WEBKIT_TYPE_SOUP_CACHE,
1231
			     "cache-dir", cache_dir,
1232
			     "cache-type", cache_type,
1233
			     NULL);
1234
}
1235
1236
/**
1237
 * webkit_soup_cache_has_response:
1238
 * @cache: a #WebKitSoupCache
1239
 * @msg: a #SoupMessage
1240
 *
1241
 * This function calculates whether the @cache object has a proper
1242
 * response for the request @msg given the flags both in the request
1243
 * and the cached reply and the time ellapsed since it was cached.
1244
 *
1245
 * Returns: whether or not the @cache has a valid response for @msg
1246
 **/
1247
WebKitSoupCacheResponse
1248
webkit_soup_cache_has_response (WebKitSoupCache *cache, SoupMessage *msg)
1249
{
1250
	char *key;
1251
	WebKitSoupCacheEntry *entry;
1252
	const char *cache_control;
1253
	GHashTable *hash;
1254
	gpointer value;
1255
	gboolean must_revalidate;
1256
	int max_age, max_stale, min_fresh;
1257
	GList *lru_item, *item;
1258
1259
	key = soup_message_get_cache_key (msg);
1260
	entry = g_hash_table_lookup (cache->priv->cache, key);
1261
1262
	/* 1. The presented Request-URI and that of stored response
1263
	 * match
1264
	 */
1265
	if (!entry)
1266
		return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
1267
1268
	/* Increase hit count. Take sorting into account */
1269
	entry->hits++;
1270
	lru_item = g_list_find (cache->priv->lru_start, entry);
1271
	item = lru_item;
1272
	while (item->next && lru_compare_func (item->data, item->next->data) > 0)
1273
		item = g_list_next (item);
1274
1275
	if (item != lru_item) {
1276
		cache->priv->lru_start = g_list_remove_link (cache->priv->lru_start, lru_item);
1277
		item = g_list_insert_sorted (item, lru_item->data, lru_compare_func);
1278
		g_list_free (lru_item);
1279
	}
1280
1281
	if (entry->dirty || entry->being_validated)
1282
		return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
1283
1284
	/* 2. The request method associated with the stored response
1285
	 *  allows it to be used for the presented request
1286
	 */
1287
1288
	/* In practice this means we only return our resource for GET,
1289
	 * cacheability for other methods is a TODO in the RFC
1290
	 * (TODO: although we could return the headers for HEAD
1291
	 * probably).
1292
	 */
1293
	if (msg->method != SOUP_METHOD_GET)
1294
		return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
1295
1296
	/* 3. Selecting request-headers nominated by the stored
1297
	 * response (if any) match those presented.
1298
	 */
1299
1300
	/* TODO */
1301
1302
	/* 4. The presented request and stored response are free from
1303
	 * directives that would prevent its use.
1304
	 */
1305
1306
	must_revalidate = FALSE;
1307
	max_age = max_stale = min_fresh = -1;
1308
1309
	cache_control = soup_message_headers_get (msg->request_headers, "Cache-Control");
1310
	if (cache_control) {
1311
		hash = soup_header_parse_param_list (cache_control);
1312
1313
		if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) {
1314
			g_hash_table_destroy (hash);
1315
			return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
1316
		}
1317
1318
		if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) {
1319
			entry->must_revalidate = TRUE;
1320
		}
1321
1322
		if (g_hash_table_lookup_extended (hash, "max-age", NULL, &value)) {
1323
			max_age = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
1324
		}
1325
1326
		/* max-stale can have no value set, we need to use _extended */
1327
		if (g_hash_table_lookup_extended (hash, "max-stale", NULL, &value)) {
1328
			if (value)
1329
				max_stale = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
1330
			else
1331
				max_stale = G_MAXINT32;
1332
		}
1333
1334
		value = g_hash_table_lookup (hash, "min-fresh");
1335
		if (value)
1336
			min_fresh = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
1337
1338
		g_hash_table_destroy (hash);
1339
1340
		if (max_age != -1) {
1341
			guint current_age = webkit_soup_cache_entry_get_current_age (entry);
1342
1343
			/* If we are over max-age and max-stale is not
1344
			   set, do not use the value from the cache
1345
			   without validation */
1346
			if (max_age <= current_age && max_stale == -1)
1347
				return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
1348
		}
1349
	}
1350
1351
	/* 5. The stored response is either: fresh, allowed to be
1352
	 * served stale or succesfully validated
1353
	 */
1354
	/* TODO consider also proxy-revalidate & s-maxage */
1355
	if (entry->must_revalidate)
1356
		return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
1357
1358
	if (!webkit_soup_cache_entry_is_fresh_enough (entry, min_fresh)) {
1359
		/* Not fresh, can it be served stale? */
1360
		if (max_stale != -1) {
1361
			/* G_MAXINT32 means we accept any staleness */
1362
			if (max_stale == G_MAXINT32)
1363
				return WEBKIT_SOUP_CACHE_RESPONSE_FRESH;
1364
1365
			if ((webkit_soup_cache_entry_get_current_age (entry) - entry->freshness_lifetime) <= max_stale)
1366
				return WEBKIT_SOUP_CACHE_RESPONSE_FRESH;
1367
		}
1368
1369
		return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
1370
	}
1371
1372
	return WEBKIT_SOUP_CACHE_RESPONSE_FRESH;
1373
}
1374
1375
/**
1376
 * webkit_soup_cache_get_cacheability:
1377
 * @cache: a #WebKitSoupCache
1378
 * @msg: a #SoupMessage
1379
 *
1380
 * Calculates whether the @msg can be cached or not.
1381
 *
1382
 * Returns: a #WebKitSoupCacheability value indicating whether the @msg can be cached or not.
1383
 **/
1384
WebKitSoupCacheability
1385
webkit_soup_cache_get_cacheability (WebKitSoupCache *cache, SoupMessage *msg)
1386
{
1387
	g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), WEBKIT_SOUP_CACHE_UNCACHEABLE);
1388
	g_return_val_if_fail (SOUP_IS_MESSAGE (msg), WEBKIT_SOUP_CACHE_UNCACHEABLE);
1389
1390
	return WEBKIT_SOUP_CACHE_GET_CLASS (cache)->get_cacheability (cache, msg);
1391
}
1392
1393
static gboolean
1394
force_flush_timeout (gpointer data)
1395
{
1396
	gboolean *forced = (gboolean *)data;
1397
	*forced = TRUE;
1398
1399
	return FALSE;
1400
}
1401
1402
/**
1403
 * webkit_soup_cache_flush:
1404
 * @cache: a #WebKitSoupCache
1405
 * @session: the #SoupSession associated with the @cache
1406
 *
1407
 * This function will force all pending writes in the @cache to be
1408
 * committed to disk. For doing so it will iterate the #GMainContext
1409
 * associated with the @session (which can be the default one) as long
1410
 * as needed.
1411
 **/
1412
void
1413
webkit_soup_cache_flush (WebKitSoupCache *cache)
1414
{
1415
	GMainContext *async_context;
1416
	SoupSession *session;
1417
	guint timeout_id;
1418
	gboolean forced = FALSE;
1419
1420
	g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache));
1421
1422
	session = cache->priv->session;
1423
	g_return_if_fail (SOUP_IS_SESSION (session));
1424
	async_context = soup_session_get_async_context (session);
1425
1426
	/* We give cache 10 secs to finish */
1427
	timeout_id = g_timeout_add (10000, force_flush_timeout, &forced);
1428
1429
	while (!forced && cache->priv->n_pending > 0)
1430
		g_main_context_iteration (async_context, FALSE);
1431
1432
	if (!forced)
1433
		g_source_remove (timeout_id);
1434
	else
1435
		g_warning ("Cache flush finished despite %d pending requests", cache->priv->n_pending);
1436
}
1437
1438
static void
1439
clear_cache_item (gpointer key,
1440
		  gpointer value,
1441
		  WebKitSoupCache *cache)
1442
{
1443
	WebKitSoupCacheEntry *entry = g_hash_table_lookup (cache->priv->cache, (const gchar *)key);
1444
	if (webkit_soup_cache_entry_remove (cache, entry))
1445
		webkit_soup_cache_entry_free (entry, TRUE);
1446
}
1447
1448
/**
1449
 * webkit_soup_cache_clear:
1450
 * @cache: a #WebKitSoupCache
1451
 *
1452
 * Will remove all entries in the @cache plus all the cache files
1453
 * associated with them.
1454
 **/
1455
void
1456
webkit_soup_cache_clear (WebKitSoupCache *cache)
1457
{
1458
	GHashTable *hash;
1459
1460
	g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache));
1461
1462
	hash = cache->priv->cache;
1463
	g_return_if_fail (hash);
1464
1465
	g_hash_table_foreach (hash, (GHFunc)clear_cache_item, cache);
1466
}
1467
1468
SoupMessage *
1469
webkit_soup_cache_generate_conditional_request (WebKitSoupCache *cache, SoupMessage *original)
1470
{
1471
	SoupMessage *msg;
1472
	SoupURI *uri;
1473
	WebKitSoupCacheEntry *entry;
1474
	char *key;
1475
	const char *value;
1476
1477
	g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), NULL);
1478
	g_return_val_if_fail (SOUP_IS_MESSAGE (original), NULL);
1479
1480
	/* First copy the data we need from the original message */
1481
	uri = soup_message_get_uri (original);
1482
	msg = soup_message_new_from_uri (original->method, uri);
1483
1484
	soup_message_headers_foreach (original->request_headers,
1485
				      (SoupMessageHeadersForeachFunc)copy_headers,
1486
				      msg->request_headers);
1487
1488
	/* Now add the validator entries in the header from the cached
1489
	   data */
1490
	key = soup_message_get_cache_key (original);
1491
	entry = g_hash_table_lookup (cache->priv->cache, key);
1492
	g_free (key);
1493
1494
	g_return_val_if_fail (entry, NULL);
1495
1496
	entry->being_validated = TRUE;
1497
1498
	value = soup_message_headers_get (entry->headers, "Last-Modified");
1499
	if (value)
1500
		soup_message_headers_append (msg->request_headers,
1501
					     "If-Modified-Since",
1502
					     value);
1503
	value = soup_message_headers_get (entry->headers, "ETag");
1504
	if (value)
1505
		soup_message_headers_append (msg->request_headers,
1506
					     "If-None-Match",
1507
					     value);
1508
	return msg;
1509
}
1510
1511
#define WEBKIT_SOUP_CACHE_FILE "soup.cache"
1512
1513
#define WEBKIT_SOUP_CACHE_HEADERS_FORMAT "{ss}"
1514
#define WEBKIT_SOUP_CACHE_PHEADERS_FORMAT "(ssbuuuuua" WEBKIT_SOUP_CACHE_HEADERS_FORMAT ")"
1515
#define WEBKIT_SOUP_CACHE_ENTRIES_FORMAT "a" WEBKIT_SOUP_CACHE_PHEADERS_FORMAT
1516
1517
/* Basically the same format than above except that some strings are
1518
   prepended with &. This way the GVariant returns a pointer to the
1519
   data instead of duplicating the string */
1520
#define WEBKIT_SOUP_CACHE_DECODE_HEADERS_FORMAT "{&s&s}"
1521
1522
static void
1523
pack_entry (gpointer data,
1524
	    gpointer user_data)
1525
{
1526
	WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *) data;
1527
	SoupMessageHeadersIter iter;
1528
	const gchar *header_key, *header_value;
1529
	GVariantBuilder *headers_builder;
1530
	GVariantBuilder *entries_builder = (GVariantBuilder *)user_data;
1531
1532
	/* Do not store non-consolidated entries */
1533
	if (entry->dirty || entry->writing || !entry->key)
1534
		return;
1535
1536
	/* Pack headers */
1537
	headers_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
1538
	soup_message_headers_iter_init (&iter, entry->headers);
1539
	while (soup_message_headers_iter_next (&iter, &header_key, &header_value)) {
1540
		if (g_utf8_validate (header_value, -1, NULL))
1541
			g_variant_builder_add (headers_builder, WEBKIT_SOUP_CACHE_HEADERS_FORMAT,
1542
					       header_key, header_value);
1543
	}
1544
1545
	/* Entry data */
1546
	g_variant_builder_add (entries_builder, WEBKIT_SOUP_CACHE_PHEADERS_FORMAT,
1547
			       entry->key, entry->filename, entry->must_revalidate,
1548
			       entry->freshness_lifetime, entry->corrected_initial_age,
1549
			       entry->response_time, entry->hits, entry->length, headers_builder);
1550
1551
	g_variant_builder_unref (headers_builder);
1552
}
1553
1554
void
1555
webkit_soup_cache_dump (WebKitSoupCache *cache)
1556
{
1557
	WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
1558
	gchar *filename;
1559
	GVariantBuilder *entries_builder;
1560
	GVariant *cache_variant;
1561
1562
	if (!g_list_length (cache->priv->lru_start))
1563
		return;
1564
1565
	/* Create the builder and iterate over all entries */
1566
	entries_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
1567
	g_list_foreach (cache->priv->lru_start, pack_entry, entries_builder);
1568
1569
	/* Serialize and dump */
1570
	cache_variant = g_variant_new (WEBKIT_SOUP_CACHE_ENTRIES_FORMAT, entries_builder);
1571
	g_variant_builder_unref (entries_builder);
1572
1573
	filename = g_build_filename (priv->cache_dir, WEBKIT_SOUP_CACHE_FILE, NULL);
1574
	g_file_set_contents (filename, (const gchar *)g_variant_get_data (cache_variant),
1575
			     g_variant_get_size (cache_variant), NULL);
1576
	g_free (filename);
1577
	g_variant_unref (cache_variant);
1578
}
1579
1580
void
1581
webkit_soup_cache_load (WebKitSoupCache *cache)
1582
{
1583
	gchar *filename = NULL, *contents = NULL;
1584
	GVariant *cache_variant;
1585
	GVariantIter *entries_iter, *headers_iter;
1586
	GVariantType *variant_format;
1587
	gsize length;
1588
	WebKitSoupCacheEntry *entry;
1589
	WebKitSoupCachePrivate *priv = cache->priv;
1590
1591
	filename = g_build_filename (priv->cache_dir, WEBKIT_SOUP_CACHE_FILE, NULL);
1592
	if (!g_file_get_contents (filename, &contents, &length, NULL)) {
1593
		g_free (filename);
1594
		return;
1595
	}
1596
	g_free (filename);
1597
1598
	variant_format = g_variant_type_new (WEBKIT_SOUP_CACHE_ENTRIES_FORMAT);
1599
	cache_variant = g_variant_new_from_data (variant_format, (const gchar *)contents, length, FALSE, NULL, NULL);
1600
	g_variant_type_free (variant_format);
1601
1602
	g_variant_get (cache_variant, WEBKIT_SOUP_CACHE_ENTRIES_FORMAT, &entries_iter);
1603
	entry = g_slice_new0 (WebKitSoupCacheEntry);
1604
1605
	while (g_variant_iter_loop (entries_iter, WEBKIT_SOUP_CACHE_PHEADERS_FORMAT,
1606
				    &entry->key, &entry->filename, &entry->must_revalidate,
1607
				    &entry->freshness_lifetime, &entry->corrected_initial_age,
1608
				    &entry->response_time, &entry->hits, &entry->length,
1609
                                    &headers_iter)) {
1610
		const gchar *header_key, *header_value;
1611
1612
		/* SoupMessage Headers */
1613
		entry->headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
1614
		while (g_variant_iter_loop (headers_iter, WEBKIT_SOUP_CACHE_DECODE_HEADERS_FORMAT, &header_key, &header_value))
1615
			soup_message_headers_append (entry->headers, header_key, header_value);
1616
1617
		/* Insert in cache */
1618
		if (!webkit_soup_cache_entry_insert_by_key (cache, (const gchar *)entry->key, entry, FALSE))
1619
			webkit_soup_cache_entry_free (entry, TRUE);
1620
1621
		/* New entry for the next iteration. This creates an
1622
		   extra object the last iteration but it's worth it
1623
		   as we save several if's */
1624
		entry = g_slice_new0 (WebKitSoupCacheEntry);
1625
	}
1626
	/* Remove last created entry */
1627
	g_slice_free (WebKitSoupCacheEntry, entry);
1628
1629
	/* Sort LRU (shouldn't be needed). First reverse as elements
1630
	 * are always prepended when inserting
1631
	 */
1632
	cache->priv->lru_start = g_list_reverse (cache->priv->lru_start);
1633
	cache->priv->lru_start = g_list_sort (cache->priv->lru_start, lru_compare_func);
1634
1635
	/* frees */
1636
	g_variant_iter_free (entries_iter);
1637
	g_variant_unref (cache_variant);
1638
}
1639
1640
void
1641
webkit_soup_cache_set_max_size (WebKitSoupCache *cache,
1642
				guint max_size)
1643
{
1644
	cache->priv->max_size = max_size;
1645
	cache->priv->max_entry_data_size = cache->priv->max_size / MAX_ENTRY_DATA_PERCENTAGE;
1646
}
1647
1648
guint
1649
webkit_soup_cache_get_max_size (WebKitSoupCache *cache)
1650
{
1651
	return cache->priv->max_size;
1652
}
- a/WebCore/platform/network/soup/cache/webkit/soup-cache.h +95 lines
Line 0 a/WebCore/platform/network/soup/cache/webkit/soup-cache.h_sec1
1
/*
2
 * soup-cache.h:
3
 *
4
 * Copyright (C) 2009, 2010 Igalia, S.L.
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Library General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Library General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Library General Public License
17
 * along with this library; see the file COPYING.LIB.  If not, write to
18
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
 * Boston, MA 02110-1301, USA.
20
 */
21
22
#ifndef WEBKIT_SOUP_CACHE_H
23
#define WEBKIT_SOUP_CACHE_H 1
24
25
#include <webkit/webkitdefines.h>
26
27
#include <libsoup/soup-types.h>
28
#include <gio/gio.h>
29
30
G_BEGIN_DECLS
31
32
#define WEBKIT_TYPE_SOUP_CACHE            (webkit_soup_cache_get_type ())
33
#define WEBKIT_SOUP_CACHE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCache))
34
#define WEBKIT_SOUP_CACHE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCacheClass))
35
#define WEBKIT_IS_SOUP_CACHE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_CACHE))
36
#define WEBKIT_IS_SOUP_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), WEBKIT_TYPE_SOUP_CACHE))
37
#define WEBKIT_SOUP_CACHE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCacheClass))
38
39
typedef struct _WebKitSoupCache WebKitSoupCache;
40
typedef struct _WebKitSoupCachePrivate WebKitSoupCachePrivate;
41
42
typedef enum {
43
	WEBKIT_SOUP_CACHE_CACHEABLE = (1 << 0),
44
	WEBKIT_SOUP_CACHE_UNCACHEABLE = (1 << 1),
45
	WEBKIT_SOUP_CACHE_INVALIDATES = (1 << 2),
46
	WEBKIT_SOUP_CACHE_VALIDATES = (1 << 3)
47
} WebKitSoupCacheability;
48
49
typedef enum {
50
	WEBKIT_SOUP_CACHE_RESPONSE_FRESH,
51
	WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION,
52
	WEBKIT_SOUP_CACHE_RESPONSE_STALE
53
} WebKitSoupCacheResponse;
54
55
typedef enum {
56
	WEBKIT_SOUP_CACHE_SINGLE_USER,
57
	WEBKIT_SOUP_CACHE_SHARED
58
} WebKitSoupCacheType;
59
60
struct _WebKitSoupCache {
61
	GObject parent_instance;
62
63
	WebKitSoupCachePrivate *priv;
64
};
65
66
typedef struct {
67
	GObjectClass parent_class;
68
69
	/* methods */
70
	WebKitSoupCacheability (*get_cacheability)(WebKitSoupCache *cache, SoupMessage *msg);
71
72
	/* Padding for future expansion */
73
	void (*_libsoup_reserved1)(void);
74
	void (*_libsoup_reserved2)(void);
75
	void (*_libsoup_reserved3)(void);
76
} WebKitSoupCacheClass;
77
78
WEBKIT_API GType             webkit_soup_cache_get_type (void);
79
WEBKIT_API WebKitSoupCache  *webkit_soup_cache_new (const char  *cache_dir,
80
						    WebKitSoupCacheType cache_type);
81
WEBKIT_API void              webkit_soup_cache_flush (WebKitSoupCache   *cache);
82
WEBKIT_API void              webkit_soup_cache_clear (WebKitSoupCache   *cache);
83
84
WEBKIT_API void              webkit_soup_cache_dump (WebKitSoupCache *cache);
85
WEBKIT_API void              webkit_soup_cache_load (WebKitSoupCache *cache);
86
87
WEBKIT_API void              webkit_soup_cache_set_max_size (WebKitSoupCache *cache,
88
							     guint max_size);
89
WEBKIT_API guint             webkit_soup_cache_get_max_size (WebKitSoupCache *cache);
90
91
G_END_DECLS
92
93
94
#endif /* WEBKIT_SOUP_CACHE_H */
95
- a/WebKitTools/ChangeLog +9 lines
Lines 1-3 a/WebKitTools/ChangeLog_sec1
1
2010-09-06  Sergio Villar Senin  <svillar@igalia.com>
2
3
        Reviewed by NOBODY (OOPS!).
4
5
        [GTK] Add HTTP caching support
6
        https://bugs.webkit.org/show_bug.cgi?id=44261
7
8
        * GNUmakefile.am: added paths for the new soup HTTP cache code
9
1
2010-09-19  Sam Weinig  <sam@webkit.org>
10
2010-09-19  Sam Weinig  <sam@webkit.org>
2
11
3
        Reviewed by Anders Carlsson.
12
        Reviewed by Anders Carlsson.
- a/WebKitTools/GNUmakefile.am +2 lines
Lines 6-11 noinst_PROGRAMS += \ a/WebKitTools/GNUmakefile.am_sec1
6
# GtkLauncher
6
# GtkLauncher
7
Programs_GtkLauncher_CPPFLAGS = \
7
Programs_GtkLauncher_CPPFLAGS = \
8
	-I$(srcdir)/WebKit/gtk \
8
	-I$(srcdir)/WebKit/gtk \
9
	-I$(srcdir)/WebCore/platform/network/soup/cache/ \
9
	-I$(top_builddir)/WebKit/gtk \
10
	-I$(top_builddir)/WebKit/gtk \
10
	-I$(top_builddir)/DerivedSources \
11
	-I$(top_builddir)/DerivedSources \
11
	$(global_cppflags) \
12
	$(global_cppflags) \
Lines 37-42 dumprendertree_cppflags := \ a/WebKitTools/GNUmakefile.am_sec2
37
	-I$(srcdir)/WebKitTools/DumpRenderTree/gtk \
38
	-I$(srcdir)/WebKitTools/DumpRenderTree/gtk \
38
	-I$(srcdir)/WebKit/gtk \
39
	-I$(srcdir)/WebKit/gtk \
39
	-I$(srcdir)/WebCore/platform/gtk \
40
	-I$(srcdir)/WebCore/platform/gtk \
41
	-I$(srcdir)/WebCore/platform/network/soup/cache/ \
40
	-I$(top_builddir)/WebKit/gtk \
42
	-I$(top_builddir)/WebKit/gtk \
41
	-I$(top_builddir)/DerivedSources \
43
	-I$(top_builddir)/DerivedSources \
42
	$(global_cppflags) \
44
	$(global_cppflags) \
- a/autotools/webkit.m4 -2 / +1 lines
Lines 108-114 AC_DEFUN_ONCE([_WEBKIT_CHECK_GLIB], a/autotools/webkit.m4_sec1
108
[dnl
108
[dnl
109
dnl check for glib
109
dnl check for glib
110
# Version requirements
110
# Version requirements
111
GLIB_REQUIRED_VERSION=2.21.3
111
GLIB_REQUIRED_VERSION=2.24
112
GOBJECT_REQUIRED_VERSION=2.0
112
GOBJECT_REQUIRED_VERSION=2.0
113
GTHREAD_REQUIRED_VERSION=2.0
113
GTHREAD_REQUIRED_VERSION=2.0
114
114
115
- 

Return to Bug 44261