| 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-04  Lucas De Marchi  <lucas.demarchi@profusion.mobi>
11
2010-09-04  Lucas De Marchi  <lucas.demarchi@profusion.mobi>
2
12
3
        Reviewed by Kenneth Rohde Christiansen.
13
        Reviewed by Kenneth Rohde Christiansen.
- a/GNUmakefile.am -1 / +3 lines
Lines 320-326 webkitgtk_static_h_api += \ a/GNUmakefile.am_sec1
320
	$(srcdir)/WebKit/gtk/webkit/webkitwebwindowfeatures.h \
320
	$(srcdir)/WebKit/gtk/webkit/webkitwebwindowfeatures.h \
321
	$(srcdir)/WebKit/gtk/webkit/webkitwebview.h \
321
	$(srcdir)/WebKit/gtk/webkit/webkitwebview.h \
322
	$(srcdir)/WebKit/gtk/webkit/webkitwebdatabase.h \
322
	$(srcdir)/WebKit/gtk/webkit/webkitwebdatabase.h \
323
	$(srcdir)/WebKit/gtk/webkit/webkitsecurityorigin.h
323
	$(srcdir)/WebKit/gtk/webkit/webkitsecurityorigin.h \
324
	$(srcdir)/WebCore/platform/network/soup/cache/webkit/soup-cache.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 542-547 webkit_tests_cflags = \ a/GNUmakefile.am_sec2
542
	-fno-strict-aliasing \
543
	-fno-strict-aliasing \
543
	-I$(srcdir)/JavaScriptCore/ForwardingHeaders \
544
	-I$(srcdir)/JavaScriptCore/ForwardingHeaders \
544
	-I$(srcdir)/WebKit/gtk \
545
	-I$(srcdir)/WebKit/gtk \
546
	-I$(srcdir)/WebCore/platform/network/soup/cache \
545
	-I$(top_builddir)/WebKit/gtk \
547
	-I$(top_builddir)/WebKit/gtk \
546
	-I$(top_builddir)/DerivedSources \
548
	-I$(top_builddir)/DerivedSources \
547
	-I$(top_srcdir)/WebCore/bindings \
549
	-I$(top_srcdir)/WebCore/bindings \
- 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-08-31  Yury Semikhatsky  <yurys@chromium.org>
194
2010-08-31  Yury Semikhatsky  <yurys@chromium.org>
2
195
3
        Reviewed by Joseph Pecoraro.
196
        Reviewed by Joseph Pecoraro.
- a/WebCore/GNUmakefile.am -1 / +19 lines
Lines 77-83 webcoregtk_cppflags += \ a/WebCore/GNUmakefile.am_sec1
77
	-I$(srcdir)/WebCore/platform/graphics/gstreamer \
77
	-I$(srcdir)/WebCore/platform/graphics/gstreamer \
78
	-I$(srcdir)/WebCore/platform/graphics/gtk \
78
	-I$(srcdir)/WebCore/platform/graphics/gtk \
79
	-I$(srcdir)/WebCore/platform/gtk \
79
	-I$(srcdir)/WebCore/platform/gtk \
80
	-I$(srcdir)/WebCore/platform/network/soup
80
	-I$(srcdir)/WebCore/platform/network/soup \
81
	-I$(srcdir)/WebCore/platform/network/soup/cache \
82
	-I$(srcdir)/WebCore/platform/network/soup/cache/webkit
81
83
82
webcore_built_nosources += \
84
webcore_built_nosources += \
83
	DerivedSources/WebCore/DocTypeStrings.cpp \
85
	DerivedSources/WebCore/DocTypeStrings.cpp \
Lines 2554-2559 webcoregtk_sources += \ a/WebCore/GNUmakefile.am_sec2
2554
	WebCore/platform/network/soup/ResourceRequestSoup.cpp \
2556
	WebCore/platform/network/soup/ResourceRequestSoup.cpp \
2555
	WebCore/platform/network/soup/ResourceResponse.h \
2557
	WebCore/platform/network/soup/ResourceResponse.h \
2556
	WebCore/platform/network/soup/ResourceResponseSoup.cpp \
2558
	WebCore/platform/network/soup/ResourceResponseSoup.cpp \
2559
	WebCore/platform/network/soup/cache/soup-directory-input-stream.c \
2560
	WebCore/platform/network/soup/cache/soup-directory-input-stream.h \
2561
	WebCore/platform/network/soup/cache/soup-http-input-stream.c \
2562
	WebCore/platform/network/soup/cache/soup-http-input-stream.h \
2563
	WebCore/platform/network/soup/cache/soup-request.c \
2564
	WebCore/platform/network/soup/cache/soup-request-data.c \
2565
	WebCore/platform/network/soup/cache/soup-request-data.h \
2566
	WebCore/platform/network/soup/cache/soup-requester.c \
2567
	WebCore/platform/network/soup/cache/soup-requester.h \
2568
	WebCore/platform/network/soup/cache/soup-request-file.c \
2569
	WebCore/platform/network/soup/cache/soup-request-file.h \
2570
	WebCore/platform/network/soup/cache/soup-request.h \
2571
	WebCore/platform/network/soup/cache/soup-request-http.c \
2572
	WebCore/platform/network/soup/cache/soup-request-http.h \
2573
	WebCore/platform/network/soup/cache/webkit/soup-cache.c \
2574
	WebCore/platform/network/soup/cache/webkit/soup-cache.h \
2557
	WebCore/plugins/gtk/PluginDataGtk.cpp \
2575
	WebCore/plugins/gtk/PluginDataGtk.cpp \
2558
	WebCore/plugins/gtk/PluginPackageGtk.cpp \
2576
	WebCore/plugins/gtk/PluginPackageGtk.cpp \
2559
	WebCore/plugins/gtk/PluginViewGtk.cpp \
2577
	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 (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 +1603 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
	GList *lru;
64
	GCancellable *cancellable;
65
} WebKitSoupCacheEntry;
66
67
struct _WebKitSoupCachePrivate {
68
	char *cache_dir;
69
	GHashTable *cache;
70
	guint n_pending;
71
	SoupSession *session;
72
	WebKitSoupCacheType cache_type;
73
	guint size;
74
	guint max_size;
75
	guint max_entry_data_size; /* Computed value. Here for performance reasons */
76
	GList *lru_start;
77
	GList *lru_end;
78
};
79
80
typedef struct {
81
	WebKitSoupCache *cache;
82
	WebKitSoupCacheEntry *entry;
83
	SoupMessage *msg;
84
	gulong got_chunk_handler;
85
	gulong got_body_handler;
86
	gulong restarted_handler;
87
} WebKitSoupCacheWritingFixture;
88
89
enum {
90
	PROP_0,
91
	PROP_CACHE_DIR,
92
	PROP_CACHE_TYPE
93
};
94
95
#define WEBKIT_SOUP_CACHE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCachePrivate))
96
97
G_DEFINE_TYPE_WITH_CODE (WebKitSoupCache, webkit_soup_cache, G_TYPE_OBJECT,
98
			 G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
99
						webkit_soup_cache_session_feature_init))
100
101
static void webkit_soup_cache_entry_delete (WebKitSoupCache *cache, WebKitSoupCacheEntry *entry);
102
static void make_room_for_new_entry (WebKitSoupCache *cache, guint length_to_add);
103
static gboolean cache_accepts_entries_of_size (WebKitSoupCache *cache, guint length_to_add);
104
105
static WebKitSoupCacheability
106
get_cacheability (WebKitSoupCache *cache, SoupMessage *msg)
107
{
108
	WebKitSoupCacheability cacheability;
109
	const char *cache_control;
110
111
	/* 1. The request method must be cacheable */
112
	if (msg->method == SOUP_METHOD_GET)
113
		cacheability = WEBKIT_SOUP_CACHE_CACHEABLE;
114
	else if (msg->method == SOUP_METHOD_HEAD ||
115
		 msg->method == SOUP_METHOD_TRACE ||
116
		 msg->method == SOUP_METHOD_CONNECT)
117
		return WEBKIT_SOUP_CACHE_UNCACHEABLE;
118
	else
119
		return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES);
120
121
	cache_control = soup_message_headers_get (msg->response_headers, "Cache-Control");
122
	if (cache_control) {
123
		GHashTable *hash;
124
		WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
125
126
		hash = soup_header_parse_param_list (cache_control);
127
128
		/* Shared caches MUST NOT store private resources */
129
		if (priv->cache_type == WEBKIT_SOUP_CACHE_SHARED) {
130
			if (g_hash_table_lookup_extended (hash, "private", NULL, NULL)) {
131
				soup_header_free_param_list (hash);
132
				return WEBKIT_SOUP_CACHE_UNCACHEABLE;
133
			}
134
		}
135
136
		/* 2. The 'no-store' cache directive does not appear in the
137
		 * headers
138
		 */
139
		if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) {
140
			soup_header_free_param_list (hash);
141
			return WEBKIT_SOUP_CACHE_UNCACHEABLE;
142
		}
143
144
		/* This does not appear in section 2.1, but I think it makes
145
		 * sense to check it too?
146
		 */
147
		if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) {
148
			soup_header_free_param_list (hash);
149
			return WEBKIT_SOUP_CACHE_UNCACHEABLE;
150
		}
151
	}
152
153
	switch (msg->status_code) {
154
		case SOUP_STATUS_PARTIAL_CONTENT:
155
			/* We don't cache partial responses, but they only
156
			 * invalidate cached full responses if the headers
157
			 * don't match.
158
			 */
159
			cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE;
160
			break;
161
162
		case SOUP_STATUS_NOT_MODIFIED:
163
			/* A 304 response validates an existing cache entry */
164
			cacheability = WEBKIT_SOUP_CACHE_VALIDATES;
165
			break;
166
167
		case SOUP_STATUS_MULTIPLE_CHOICES:
168
		case SOUP_STATUS_MOVED_PERMANENTLY:
169
		case SOUP_STATUS_GONE:
170
			/* FIXME: cacheable unless indicated otherwise */
171
			cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE;
172
			break;
173
174
		case SOUP_STATUS_FOUND:
175
		case SOUP_STATUS_TEMPORARY_REDIRECT:
176
			/* FIXME: cacheable if explicitly indicated */
177
			cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE;
178
			break;
179
180
		case SOUP_STATUS_SEE_OTHER:
181
		case SOUP_STATUS_FORBIDDEN:
182
		case SOUP_STATUS_NOT_FOUND:
183
		case SOUP_STATUS_METHOD_NOT_ALLOWED:
184
			return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES);
185
186
		default:
187
			/* Any 5xx status or any 4xx status not handled above
188
			 * is uncacheable but doesn't break the cache.
189
			 */
190
			if ((msg->status_code >= SOUP_STATUS_BAD_REQUEST &&
191
			     msg->status_code <= SOUP_STATUS_FAILED_DEPENDENCY) ||
192
			    msg->status_code >= SOUP_STATUS_INTERNAL_SERVER_ERROR)
193
				return WEBKIT_SOUP_CACHE_UNCACHEABLE;
194
195
			/* An unrecognized 2xx, 3xx, or 4xx response breaks
196
			 * the cache.
197
			 */
198
			if ((msg->status_code > SOUP_STATUS_PARTIAL_CONTENT &&
199
			     msg->status_code < SOUP_STATUS_MULTIPLE_CHOICES) ||
200
			    (msg->status_code > SOUP_STATUS_TEMPORARY_REDIRECT &&
201
			     msg->status_code < SOUP_STATUS_INTERNAL_SERVER_ERROR))
202
				return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES);
203
			break;
204
	}
205
206
	return cacheability;
207
}
208
209
static void
210
webkit_soup_cache_entry_free (WebKitSoupCacheEntry *entry)
211
{
212
	g_free (entry->filename);
213
	entry->filename = NULL;
214
	g_free (entry->key);
215
	entry->key = NULL;
216
217
	if (entry->headers) {
218
		soup_message_headers_free (entry->headers);
219
		entry->headers = NULL;
220
	}
221
222
	if (entry->data) {
223
		g_string_free (entry->data, TRUE);
224
		entry->data = NULL;
225
	}
226
	if (entry->error) {
227
		g_error_free (entry->error);
228
		entry->error = NULL;
229
	}
230
	if (entry->cancellable) {
231
		g_object_unref (entry->cancellable);
232
		entry->cancellable = NULL;
233
	}
234
235
	g_slice_free (WebKitSoupCacheEntry, entry);
236
}
237
238
static void
239
copy_headers (const char *name, const char *value, SoupMessageHeaders *headers)
240
{
241
	soup_message_headers_append (headers, name, value);
242
}
243
244
static void
245
update_headers (const char *name, const char *value, SoupMessageHeaders *headers)
246
{
247
	if (soup_message_headers_get (headers, name))
248
		soup_message_headers_replace (headers, name, value);
249
	else
250
		soup_message_headers_append (headers, name, value);
251
}
252
253
static guint
254
webkit_soup_cache_entry_get_current_age (WebKitSoupCacheEntry *entry)
255
{
256
	time_t now = time (NULL);
257
	time_t resident_time;
258
259
	resident_time = now - entry->response_time;
260
	return entry->corrected_initial_age + resident_time;
261
}
262
263
static gboolean
264
webkit_soup_cache_entry_is_fresh_enough (WebKitSoupCacheEntry *entry, int min_fresh)
265
{
266
	unsigned limit = (min_fresh == -1) ? webkit_soup_cache_entry_get_current_age (entry) : min_fresh;
267
	return entry->freshness_lifetime > limit;
268
}
269
270
static char *
271
soup_message_get_cache_key (SoupMessage *msg)
272
{
273
	SoupURI *uri = soup_message_get_uri (msg);
274
	return soup_uri_to_string (uri, FALSE);
275
}
276
277
static void
278
webkit_soup_cache_entry_set_freshness (WebKitSoupCacheEntry *entry, SoupMessage *msg, WebKitSoupCache *cache)
279
{
280
	const char *cache_control;
281
	const char *expires, *date, *last_modified;
282
	GHashTable *hash;
283
284
	hash = NULL;
285
286
	cache_control = soup_message_headers_get (entry->headers, "Cache-Control");
287
	if (cache_control) {
288
		const char *max_age, *s_maxage;
289
		gint64 freshness_lifetime = 0;
290
		WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
291
292
		hash = soup_header_parse_param_list (cache_control);
293
294
		/* Should we re-validate the entry when it goes stale */
295
		entry->must_revalidate = (gboolean)g_hash_table_lookup (hash, "must-revalidate");
296
297
		/* Section 2.3.1 */
298
		if (priv->cache_type == WEBKIT_SOUP_CACHE_SHARED) {
299
			s_maxage = g_hash_table_lookup (hash, "s-maxage");
300
			if (s_maxage) {
301
				freshness_lifetime = g_ascii_strtoll (s_maxage, NULL, 10);
302
				if (freshness_lifetime) {
303
					/* Implies proxy-revalidate. TODO: is it true? */
304
					entry->must_revalidate = TRUE;
305
					soup_header_free_param_list (hash);
306
					return;
307
				}
308
			}
309
		}
310
311
		/* If 'max-age' cache directive is present, use that */
312
		max_age = g_hash_table_lookup (hash, "max-age");
313
		if (max_age)
314
			freshness_lifetime = g_ascii_strtoll (max_age, NULL, 10);
315
316
		if (freshness_lifetime) {
317
			entry->freshness_lifetime = (guint)MIN (freshness_lifetime, G_MAXUINT32);
318
			soup_header_free_param_list (hash);
319
			return;
320
		}
321
	}
322
323
	if (hash != NULL)
324
		soup_header_free_param_list (hash);
325
326
	/* If the 'Expires' response header is present, use its value
327
	 * minus the value of the 'Date' response header
328
	 */
329
	expires = soup_message_headers_get (entry->headers, "Expires");
330
	date = soup_message_headers_get (entry->headers, "Date");
331
	if (expires && date) {
332
		SoupDate *expires_d, *date_d;
333
		time_t expires_t, date_t;
334
335
		expires_d = soup_date_new_from_string (expires);
336
		if (expires_d) {
337
			date_d = soup_date_new_from_string (date);
338
339
			expires_t = soup_date_to_time_t (expires_d);
340
			date_t = soup_date_to_time_t (date_d);
341
342
			soup_date_free (expires_d);
343
			soup_date_free (date_d);
344
345
			if (expires_t && date_t) {
346
				entry->freshness_lifetime = (guint)MAX (expires_t - date_t, 0);
347
				return;
348
			}
349
		} else {
350
			/* If Expires is not a valid date we should
351
			   treat it as already expired, see section
352
			   3.3 */
353
			entry->freshness_lifetime = 0;
354
			return;
355
		}
356
	}
357
358
	/* Otherwise an heuristic may be used */
359
360
	/* Heuristics MUST NOT be used with these status codes
361
	   (section 2.3.1.1) */
362
	if (msg->status_code != SOUP_STATUS_OK &&
363
	    msg->status_code != SOUP_STATUS_NON_AUTHORITATIVE &&
364
	    msg->status_code != SOUP_STATUS_PARTIAL_CONTENT &&
365
	    msg->status_code != SOUP_STATUS_MULTIPLE_CHOICES &&
366
	    msg->status_code != SOUP_STATUS_MOVED_PERMANENTLY &&
367
	    msg->status_code != SOUP_STATUS_GONE)
368
		goto expire;
369
370
	/* TODO: attach warning 113 if response's current_age is more
371
	   than 24h (section 2.3.1.1) when using heuristics */
372
373
	/* Last-Modified based heuristic */
374
	last_modified = soup_message_headers_get (entry->headers, "Last-Modified");
375
	if (last_modified) {
376
		SoupDate *soup_date;
377
		time_t now, last_modified_t;
378
379
		soup_date = soup_date_new_from_string (last_modified);
380
		last_modified_t = soup_date_to_time_t (soup_date);
381
		now = time (NULL);
382
383
#define HEURISTIC_FACTOR 0.1 /* From Section 2.3.1.1 */
384
385
		entry->freshness_lifetime = MAX (0, (now - last_modified_t) * HEURISTIC_FACTOR);
386
		soup_date_free (soup_date);
387
	}
388
389
	return;
390
391
 expire:
392
	/* If all else fails, make the entry expire immediately */
393
	entry->freshness_lifetime = 0;
394
}
395
396
static WebKitSoupCacheEntry *
397
webkit_soup_cache_entry_new (WebKitSoupCache *cache, SoupMessage *msg, time_t request_time, time_t response_time)
398
{
399
	WebKitSoupCacheEntry *entry;
400
	SoupMessageHeaders *headers;
401
	const char *date;
402
	char *md5;
403
404
	entry = g_slice_new0 (WebKitSoupCacheEntry);
405
	entry->dirty = FALSE;
406
	entry->writing = FALSE;
407
	entry->got_body = FALSE;
408
	entry->being_validated = FALSE;
409
	entry->data = g_string_new (NULL);
410
	entry->pos = 0;
411
	entry->error = NULL;
412
413
	/* key & filename */
414
	entry->key = soup_message_get_cache_key (msg);
415
	md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, entry->key, -1);
416
	entry->filename = g_build_filename (cache->priv->cache_dir, md5, NULL);
417
	g_free (md5);
418
419
	/* Headers */
420
	headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
421
	soup_message_headers_foreach (msg->response_headers,
422
				      (SoupMessageHeadersForeachFunc)copy_headers,
423
				      headers);
424
	entry->headers = headers;
425
426
	/* LRU list */
427
	entry->hits = 0;
428
429
	/* Section 2.3.1, Freshness Lifetime */
430
	webkit_soup_cache_entry_set_freshness (entry, msg, cache);
431
432
	/* Section 2.3.2, Calculating Age */
433
	date = soup_message_headers_get (entry->headers, "Date");
434
435
	if (date) {
436
		SoupDate *soup_date;
437
		const char *age;
438
		time_t date_value, apparent_age, corrected_received_age, response_delay, age_value = 0;
439
440
		soup_date = soup_date_new_from_string (date);
441
		date_value = soup_date_to_time_t (soup_date);
442
		soup_date_free (soup_date);
443
444
		age = soup_message_headers_get (entry->headers, "Age");
445
		if (age)
446
			age_value = g_ascii_strtoll (age, NULL, 10);
447
448
		entry->response_time = response_time;
449
		apparent_age = MAX (0, entry->response_time - date_value);
450
		corrected_received_age = MAX (apparent_age, age_value);
451
		response_delay = entry->response_time - request_time;
452
		entry->corrected_initial_age = corrected_received_age + response_delay;
453
	} else {
454
		/* Is this correct ? */
455
		entry->corrected_initial_age = time (NULL);
456
	}
457
458
	return entry;
459
}
460
461
static void
462
webkit_soup_cache_writing_fixture_free (WebKitSoupCacheWritingFixture *fixture)
463
{
464
	/* Free fixture. And disconnect signals, we don't want to
465
	   listen to more SoupMessage events as we're finished with
466
	   this resource */
467
	if (g_signal_handler_is_connected (fixture->msg, fixture->got_chunk_handler))
468
		g_signal_handler_disconnect (fixture->msg, fixture->got_chunk_handler);
469
	if (g_signal_handler_is_connected (fixture->msg, fixture->got_body_handler))
470
		g_signal_handler_disconnect (fixture->msg, fixture->got_body_handler);
471
	if (g_signal_handler_is_connected (fixture->msg, fixture->restarted_handler))
472
		g_signal_handler_disconnect (fixture->msg, fixture->restarted_handler);
473
	g_object_unref (fixture->msg);
474
	g_object_unref (fixture->cache);
475
	g_slice_free (WebKitSoupCacheWritingFixture, fixture);
476
}
477
478
static void
479
close_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture)
480
{
481
	WebKitSoupCacheEntry *entry = fixture->entry;
482
	WebKitSoupCache *cache = fixture->cache;
483
	GOutputStream *stream = G_OUTPUT_STREAM (source);
484
	goffset content_length;
485
486
	g_warn_if_fail (entry->error == NULL);
487
488
	/* FIXME: what do we do on error ? */
489
490
	if (stream) {
491
		g_output_stream_close_finish (stream, result, NULL);
492
		g_object_unref (stream);
493
	}
494
	entry->stream = NULL;
495
496
	content_length = soup_message_headers_get_content_length (entry->headers);
497
498
	/* If the process was cancelled, then delete the entry from
499
	   the cache. Do it also if the size of a chunked resource is
500
	   too much for the cache */
501
	if (g_cancellable_is_cancelled (entry->cancellable)) {
502
		webkit_soup_cache_entry_delete (cache, entry);
503
		entry = NULL;
504
	} else if ((soup_message_headers_get_encoding (entry->headers) == SOUP_ENCODING_CHUNKED) ||
505
		   entry->length != content_length) {
506
		/** Two options here:
507
		 *
508
		 * 1. "chunked" data, entry was temporarily added to
509
		 * cache (as content-length is 0) and now that we have
510
		 * the actual size we have to evaluate if we want it
511
		 * in the cache or not
512
		 *
513
		 * 2. Content-Length has a different value than actual
514
		 * length, means that the content was encoded for
515
		 * transmission (typically compressed) and thus we
516
		 * have to substract the content-length value that was
517
		 * added to the cache and add the unencoded length
518
		 **/
519
		gint length_to_add = entry->length - content_length;
520
521
		/* Make room in cache if needed */
522
		if (cache_accepts_entries_of_size (cache, length_to_add)) {
523
			make_room_for_new_entry (cache, length_to_add);
524
525
			cache->priv->size += length_to_add;
526
		} else {
527
			webkit_soup_cache_entry_delete (cache, entry);
528
			entry = NULL;
529
		}
530
	}
531
532
	if (entry) {
533
		/* Get rid of the GString in memory for the resource now */
534
		if (entry->data) {
535
			g_string_free (entry->data, TRUE);
536
			entry->data = NULL;
537
		}
538
539
		entry->dirty = FALSE;
540
		entry->writing = FALSE;
541
		entry->got_body = FALSE;
542
		entry->pos = 0;
543
544
		g_object_unref (entry->cancellable);
545
		entry->cancellable = NULL;
546
	}
547
548
	cache->priv->n_pending--;
549
550
	/* Frees */
551
	webkit_soup_cache_writing_fixture_free (fixture);
552
}
553
554
static void
555
write_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture)
556
{
557
	GOutputStream *stream = G_OUTPUT_STREAM (source);
558
	GError *error = NULL;
559
	gssize write_size;
560
	WebKitSoupCacheEntry *entry = fixture->entry;
561
562
	if (g_cancellable_is_cancelled (entry->cancellable)) {
563
		g_output_stream_close_async (stream,
564
					     G_PRIORITY_DEFAULT,
565
					     entry->cancellable,
566
					     (GAsyncReadyCallback)close_ready_cb,
567
					     fixture);
568
		return;
569
	}
570
571
	write_size = g_output_stream_write_finish (stream, result, &error);
572
	if (write_size <= 0 || error) {
573
		if (error)
574
			entry->error = error;
575
		g_output_stream_close_async (stream,
576
					     G_PRIORITY_DEFAULT,
577
					     entry->cancellable,
578
					     (GAsyncReadyCallback)close_ready_cb,
579
					     fixture);
580
		/* FIXME: We should completely stop caching the
581
		   resource at this point */
582
	} else {
583
		entry->pos += write_size;
584
585
		/* Are we still writing and is there new data to write
586
		   already ? */
587
		if (entry->data && entry->pos < entry->data->len) {
588
			g_output_stream_write_async (entry->stream,
589
						     entry->data->str + entry->pos,
590
						     entry->data->len - entry->pos,
591
						     G_PRIORITY_DEFAULT,
592
						     entry->cancellable,
593
						     (GAsyncReadyCallback)write_ready_cb,
594
						     fixture);
595
		} else {
596
			entry->writing = FALSE;
597
598
			if (entry->got_body) {
599
				/* If we already received 'got-body'
600
				   and we have written all the data,
601
				   we can close the stream */
602
				g_output_stream_close_async (entry->stream,
603
							     G_PRIORITY_DEFAULT,
604
							     entry->cancellable,
605
							     (GAsyncReadyCallback)close_ready_cb,
606
							     fixture);
607
			}
608
		}
609
	}
610
}
611
612
static void
613
msg_got_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, WebKitSoupCacheWritingFixture *fixture)
614
{
615
	WebKitSoupCacheEntry *entry = fixture->entry;
616
617
	g_return_if_fail (chunk->data && chunk->length);
618
	g_return_if_fail (entry);
619
620
	/* Ignore this if the writing or appending was cancelled */
621
	if (!g_cancellable_is_cancelled (entry->cancellable)) {
622
		g_string_append_len (entry->data, chunk->data, chunk->length);
623
		entry->length = entry->data->len;
624
625
		if (!cache_accepts_entries_of_size (fixture->cache, entry->length)) {
626
			/* Quickly cancel the caching of the resource */
627
			g_cancellable_cancel (entry->cancellable);
628
		}
629
	}
630
631
	/* FIXME: remove the error check when we cancel the caching at
632
	   the first write error */
633
	/* Only write if the entry stream is ready */
634
	if (entry->writing == FALSE && entry->error == NULL && entry->stream) {
635
		GString *data = entry->data;
636
		entry->writing = TRUE;
637
		g_output_stream_write_async (entry->stream,
638
					     data->str + entry->pos,
639
					     data->len - entry->pos,
640
					     G_PRIORITY_DEFAULT,
641
					     entry->cancellable,
642
					     (GAsyncReadyCallback)write_ready_cb,
643
					     fixture);
644
	}
645
}
646
647
static void
648
msg_got_body_cb (SoupMessage *msg, WebKitSoupCacheWritingFixture *fixture)
649
{
650
	WebKitSoupCacheEntry *entry = fixture->entry;
651
	g_return_if_fail (entry);
652
653
	entry->got_body = TRUE;
654
655
	if (!entry->stream && entry->pos != entry->length)
656
		/* The stream is not ready to be written but we still
657
		   have data to write, we'll write it when the stream
658
		   is opened for writing */
659
		return;
660
661
662
	if (entry->pos != entry->length) {
663
		/* If we still have data to write, write it,
664
		   write_ready_cb will close the stream */
665
		if (entry->writing == FALSE && entry->error == NULL && entry->stream) {
666
			g_output_stream_write_async (entry->stream,
667
						     entry->data->str + entry->pos,
668
						     entry->data->len - entry->pos,
669
						     G_PRIORITY_DEFAULT,
670
						     entry->cancellable,
671
						     (GAsyncReadyCallback)write_ready_cb,
672
						     fixture);
673
		}
674
		return;
675
	}
676
677
	if (entry->stream)
678
		g_output_stream_close_async (entry->stream,
679
					     G_PRIORITY_DEFAULT,
680
					     entry->cancellable,
681
					     (GAsyncReadyCallback)close_ready_cb,
682
					     fixture);
683
}
684
685
static void
686
webkit_soup_cache_entry_delete (WebKitSoupCache *cache, WebKitSoupCacheEntry *entry)
687
{
688
	GFile *file;
689
	char *md5, *filename;
690
691
	/* Delete cache file. We don't use the entry->filename because
692
	   we want to ensure that file is deleted even tough the cache
693
	   became somehow corrupted and the entry is not there */
694
	md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, entry->key, -1);
695
	filename = g_build_filename (cache->priv->cache_dir, md5, NULL);
696
	file = g_file_new_for_path (filename);
697
	g_file_delete (file, NULL, NULL);
698
	g_free (md5);
699
	g_free (filename);
700
	g_object_unref (file);
701
702
	/* Remove from LRU */
703
	cache->priv->lru_start = g_list_delete_link (cache->priv->lru_start, entry->lru);
704
705
	/* Remove from cache */
706
	g_hash_table_remove (cache->priv->cache, entry->key);
707
}
708
709
static gint
710
lru_compare_func (gconstpointer a, gconstpointer b)
711
{
712
	WebKitSoupCacheEntry *entry_a = (WebKitSoupCacheEntry *)a;
713
	WebKitSoupCacheEntry *entry_b = (WebKitSoupCacheEntry *)b;
714
715
	/** The rationale of this sorting func is
716
	 *
717
	 * 1. sort by hits -> LRU algorithm, then
718
	 *
719
	 * 2. sort by freshness lifetime, we better discard first
720
	 * entries that are close to expire
721
	 *
722
	 * 3. sort by size, replace first small size resources as they
723
	 * are cheaper to download
724
	 **/
725
726
	/* Sort by hits */
727
	if (entry_a->hits != entry_b->hits)
728
		return entry_a->hits - entry_b->hits;
729
730
	/* Sort by freshness_lifetime */
731
	if (entry_a->freshness_lifetime != entry_b->freshness_lifetime)
732
		return entry_a->freshness_lifetime - entry_b->freshness_lifetime;
733
734
	/* Sort by size */
735
	return entry_a->length - entry_b->length;
736
}
737
738
static gboolean
739
cache_accepts_entries_of_size (WebKitSoupCache *cache, guint length_to_add)
740
{
741
	/* We could add here some more heuristics. TODO: review how
742
	   this is done by other HTTP caches */
743
744
	return length_to_add <= cache->priv->max_entry_data_size;
745
}
746
747
static void
748
make_room_for_new_entry (WebKitSoupCache *cache, guint length_to_add)
749
{
750
	/* Check that there is enough room for the new entry. This is
751
	   an approximation as we're not working out the size of the
752
	   cache file or the size of the headers for performance
753
	   reasons. TODO: check if that would be really that expensive */
754
	while (cache->priv->lru_start &&
755
	       (length_to_add + cache->priv->size > cache->priv->max_size)) {
756
		WebKitSoupCacheEntry *old_entry;
757
758
		/* Remove from list */
759
		old_entry = (WebKitSoupCacheEntry *)cache->priv->lru_start->data;
760
		cache->priv->lru_start = g_list_delete_link (cache->priv->lru_start, cache->priv->lru_start);
761
762
		/* Discard entries. Once cancelled resources will be
763
		   freed in close_ready_cb */
764
		g_debug ("Discarding (%u bytes)...\t(now %u)", old_entry->length, cache->priv->size);
765
		cache->priv->size -= old_entry->length;
766
767
		if (old_entry->dirty)
768
			g_cancellable_cancel (old_entry->cancellable);
769
		else
770
			webkit_soup_cache_entry_delete (cache, old_entry);
771
	}
772
	if (G_UNLIKELY (cache->priv->lru_start == NULL))
773
		cache->priv->lru_end = NULL;
774
}
775
776
static gboolean
777
webkit_soup_cache_entry_insert_by_key (WebKitSoupCache *cache, const char *key, WebKitSoupCacheEntry *entry)
778
{
779
	guint length_to_add = 0;
780
781
	if (soup_message_headers_get_encoding (entry->headers) != SOUP_ENCODING_CHUNKED)
782
		length_to_add = soup_message_headers_get_content_length (entry->headers);
783
784
	/* Check if we are going to store the resource depending on its size */
785
	if (length_to_add) {
786
		if (!cache_accepts_entries_of_size (cache, length_to_add))
787
			return FALSE;
788
789
		/* Make room for new entry if needed */
790
		make_room_for_new_entry (cache, length_to_add);
791
	}
792
793
	g_hash_table_insert (cache->priv->cache, g_strdup (key), entry);
794
795
	/* Compute new cache size */
796
	cache->priv->size += length_to_add;
797
798
	/* Update LRU lists */
799
	cache->priv->lru_start = g_list_insert_sorted (cache->priv->lru_start, entry, lru_compare_func);
800
	if (G_LIKELY (cache->priv->lru_end)) {
801
		while (cache->priv->lru_end->next != NULL)
802
			cache->priv->lru_end = g_list_next (cache->priv->lru_end);
803
	} else {
804
		cache->priv->lru_end = cache->priv->lru_start;
805
	}
806
807
	return TRUE;
808
}
809
810
static void
811
msg_restarted_cb (SoupMessage *msg, WebKitSoupCacheEntry *entry)
812
{
813
	/* FIXME: What should we do here exactly? */
814
}
815
816
static void
817
append_to_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture)
818
{
819
	GFile *file = (GFile *)source;
820
	GOutputStream *stream;
821
	WebKitSoupCacheEntry *entry = fixture->entry;
822
823
	stream = (GOutputStream *)g_file_append_to_finish (file, result, &entry->error);
824
825
	if (g_cancellable_is_cancelled (entry->cancellable) || entry->error) {
826
		fixture->cache->priv->size -= soup_message_headers_get_content_length (entry->headers);
827
		fixture->cache->priv->n_pending--;
828
		webkit_soup_cache_entry_delete (fixture->cache, entry);
829
		webkit_soup_cache_writing_fixture_free (fixture);
830
		return;
831
	}
832
833
	entry->stream = g_object_ref (stream);
834
	g_object_unref (file);
835
836
	/* If we already got all the data we have to initiate the
837
	   writing here, since we won't get more 'got-chunk'
838
	   signals */
839
	if (entry->got_body) {
840
		GString *data = entry->data;
841
842
		/* It could happen that reading the data from server
843
		   was completed before this happens. In that case
844
		   there is no data */
845
		if (data) {
846
			entry->writing = TRUE;
847
			g_output_stream_write_async (entry->stream,
848
						     data->str + entry->pos,
849
						     data->len - entry->pos,
850
						     G_PRIORITY_DEFAULT,
851
						     entry->cancellable,
852
						     (GAsyncReadyCallback)write_ready_cb,
853
						     fixture);
854
		}
855
	}
856
}
857
858
typedef struct {
859
	time_t request_time;
860
	SoupSessionFeature *feature;
861
	gulong got_headers_handler;
862
} RequestHelper;
863
864
static void
865
msg_got_headers_cb (SoupMessage *msg, gpointer user_data)
866
{
867
	WebKitSoupCache *cache;
868
	WebKitSoupCacheability cacheable;
869
	RequestHelper *helper;
870
	time_t request_time, response_time;
871
872
	response_time = time (NULL);
873
874
	helper = (RequestHelper *)user_data;
875
	cache = WEBKIT_SOUP_CACHE (helper->feature);
876
	request_time = helper->request_time;
877
	g_signal_handlers_disconnect_by_func (msg, msg_got_headers_cb, user_data);
878
	g_slice_free (RequestHelper, helper);
879
880
	cacheable = webkit_soup_cache_get_cacheability (cache, msg);
881
882
	if (cacheable & WEBKIT_SOUP_CACHE_CACHEABLE) {
883
		WebKitSoupCacheEntry *entry;
884
		char *key;
885
		GFile *file;
886
		WebKitSoupCacheWritingFixture *fixture;
887
888
		/* Check if we are already caching this resource */
889
		key = soup_message_get_cache_key (msg);
890
		entry = g_hash_table_lookup (cache->priv->cache, key);
891
		g_free (key);
892
893
		if (entry && entry->dirty)
894
			return;
895
896
		/* Create a new entry, deleting any old one if present */
897
		entry = webkit_soup_cache_entry_new (cache, msg, request_time, response_time);
898
		webkit_soup_cache_entry_delete (cache, entry);
899
900
		entry->hits = 1;
901
902
		/* Do not continue if it can not be stored */
903
		if (!webkit_soup_cache_entry_insert_by_key (cache, (const gchar *)entry->key, entry)) {
904
			webkit_soup_cache_entry_free (entry);
905
			return;
906
		}
907
908
		fixture = g_slice_new (WebKitSoupCacheWritingFixture);
909
		fixture->cache = g_object_ref (cache);
910
		fixture->entry = entry;
911
		fixture->msg = g_object_ref (msg);
912
913
		/* We connect now to these signals and buffer the data
914
		   if it comes before the file is ready for writing */
915
		fixture->got_chunk_handler =
916
			g_signal_connect (msg, "got-chunk", G_CALLBACK (msg_got_chunk_cb), fixture);
917
		fixture->got_body_handler =
918
			g_signal_connect (msg, "got-body", G_CALLBACK (msg_got_body_cb), fixture);
919
		fixture->restarted_handler =
920
			g_signal_connect (msg, "restarted", G_CALLBACK (msg_restarted_cb), entry);
921
922
		/* Prepare entry */
923
		file = g_file_new_for_path (entry->filename);
924
		cache->priv->n_pending++;
925
926
		entry->dirty = TRUE;
927
		entry->cancellable = g_cancellable_new ();
928
		g_file_append_to_async (file, 0,
929
					G_PRIORITY_DEFAULT, entry->cancellable,
930
					(GAsyncReadyCallback)append_to_ready_cb,
931
					fixture);
932
	} else if (cacheable & WEBKIT_SOUP_CACHE_INVALIDATES) {
933
		char *key;
934
		WebKitSoupCacheEntry *entry;
935
936
		key = soup_message_get_cache_key (msg);
937
		entry = g_hash_table_lookup (cache->priv->cache, key);
938
		g_free (key);
939
940
		if (entry)
941
			webkit_soup_cache_entry_delete (cache, entry);
942
	} else if (cacheable & WEBKIT_SOUP_CACHE_VALIDATES) {
943
		char *key;
944
		WebKitSoupCacheEntry *entry;
945
946
		key = soup_message_get_cache_key (msg);
947
		entry = g_hash_table_lookup (cache->priv->cache, key);
948
		g_free (key);
949
950
		g_return_if_fail (entry);
951
952
		entry->being_validated = FALSE;
953
954
		/* We update the headers of the existing cache item,
955
		   plus its age */
956
		soup_message_headers_foreach (msg->response_headers,
957
					      (SoupMessageHeadersForeachFunc)update_headers,
958
					      entry->headers);
959
		webkit_soup_cache_entry_set_freshness (entry, msg, cache);
960
	}
961
}
962
963
GInputStream *
964
webkit_soup_cache_send_response (WebKitSoupCache *cache, SoupMessage *msg)
965
{
966
	char *key;
967
	WebKitSoupCacheEntry *entry;
968
	char *current_age;
969
	GInputStream *stream = NULL;
970
	GFile *file;
971
972
	g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), NULL);
973
	g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
974
975
	key = soup_message_get_cache_key (msg);
976
	entry = g_hash_table_lookup (cache->priv->cache, key);
977
	g_return_val_if_fail (entry, NULL);
978
979
	/* If we are told to send a response from cache any validation
980
	   in course is over by now */
981
	entry->being_validated = FALSE;
982
983
	/* Headers */
984
	soup_message_headers_foreach (entry->headers,
985
				      (SoupMessageHeadersForeachFunc)update_headers,
986
				      msg->response_headers);
987
988
	/* Add 'Age' header with the current age */
989
	current_age = g_strdup_printf ("%d", webkit_soup_cache_entry_get_current_age (entry));
990
	soup_message_headers_replace (msg->response_headers,
991
				      "Age",
992
				      current_age);
993
	g_free (current_age);
994
995
	/* TODO: the original idea was to save reads, but current code
996
	   assumes that a stream is always returned. Need to reach
997
	   some agreement here. Also we have to handle the situation
998
	   were the file was no longer there (for example files
999
	   removed without notifying the cache */
1000
	file = g_file_new_for_path (entry->filename);
1001
	stream = (GInputStream *)g_file_read (file, NULL, NULL);
1002
1003
	return stream;
1004
}
1005
1006
static void
1007
request_started (SoupSessionFeature *feature, SoupSession *session,
1008
		 SoupMessage *msg, SoupSocket *socket)
1009
{
1010
	RequestHelper *helper = g_slice_new0 (RequestHelper);
1011
	helper->request_time = time (NULL);
1012
	helper->feature = feature;
1013
	helper->got_headers_handler = g_signal_connect (msg, "got-headers",
1014
							G_CALLBACK (msg_got_headers_cb),
1015
							helper);
1016
}
1017
1018
static void
1019
attach (SoupSessionFeature *feature, SoupSession *session)
1020
{
1021
	WebKitSoupCache *cache = WEBKIT_SOUP_CACHE (feature);
1022
	cache->priv->session = session;
1023
1024
	webkit_soup_cache_default_feature_interface->attach (feature, session);
1025
}
1026
1027
static void
1028
webkit_soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface,
1029
					gpointer interface_data)
1030
{
1031
	webkit_soup_cache_default_feature_interface =
1032
		g_type_default_interface_peek (SOUP_TYPE_SESSION_FEATURE);
1033
1034
	feature_interface->attach = attach;
1035
	feature_interface->request_started = request_started;
1036
}
1037
1038
static void
1039
webkit_soup_cache_init (WebKitSoupCache *cache)
1040
{
1041
	WebKitSoupCachePrivate *priv;
1042
1043
	priv = cache->priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
1044
1045
	priv->cache = g_hash_table_new_full (g_str_hash,
1046
					     g_str_equal,
1047
					     (GDestroyNotify)g_free,
1048
					     (GDestroyNotify)webkit_soup_cache_entry_free);
1049
1050
	/* LRU */
1051
	priv->lru_start = NULL;
1052
	priv->lru_end = NULL;
1053
1054
	/* */
1055
	priv->n_pending = 0;
1056
1057
	/* Cache size */
1058
	priv->max_size = DEFAULT_MAX_SIZE;
1059
	priv->max_entry_data_size = priv->max_size / MAX_ENTRY_DATA_PERCENTAGE;
1060
	priv->size = 0;
1061
}
1062
1063
static void
1064
webkit_soup_cache_finalize (GObject *object)
1065
{
1066
	WebKitSoupCachePrivate *priv;
1067
1068
	priv = WEBKIT_SOUP_CACHE (object)->priv;
1069
1070
	g_hash_table_destroy (priv->cache);
1071
	g_free (priv->cache_dir);
1072
1073
	g_list_free (priv->lru_start);
1074
	priv->lru_start = NULL;
1075
	priv->lru_end = NULL;
1076
1077
	G_OBJECT_CLASS (webkit_soup_cache_parent_class)->finalize (object);
1078
}
1079
1080
static void
1081
webkit_soup_cache_set_property (GObject *object, guint prop_id,
1082
				const GValue *value, GParamSpec *pspec)
1083
{
1084
	WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE (object)->priv;
1085
1086
	switch (prop_id) {
1087
		case PROP_CACHE_DIR:
1088
			priv->cache_dir = g_value_dup_string (value);
1089
			/* Create directory if it does not exist (FIXME: should we?) */
1090
			if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
1091
				g_mkdir_with_parents (priv->cache_dir, 0700);
1092
			break;
1093
		case PROP_CACHE_TYPE:
1094
			priv->cache_type = g_value_get_enum (value);
1095
			/* TODO: clear private entries and issue a warning if moving to shared? */
1096
			break;
1097
		default:
1098
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1099
			break;
1100
	}
1101
}
1102
1103
static void
1104
webkit_soup_cache_get_property (GObject *object, guint prop_id,
1105
				GValue *value, GParamSpec *pspec)
1106
{
1107
	WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE (object)->priv;
1108
1109
	switch (prop_id) {
1110
		case PROP_CACHE_DIR:
1111
			g_value_set_string (value, priv->cache_dir);
1112
			break;
1113
		case PROP_CACHE_TYPE:
1114
			g_value_set_enum (value, priv->cache_type);
1115
			break;
1116
		default:
1117
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1118
			break;
1119
	}
1120
}
1121
1122
static void
1123
webkit_soup_cache_constructed (GObject *object)
1124
{
1125
	WebKitSoupCachePrivate *priv;
1126
1127
	priv = WEBKIT_SOUP_CACHE (object)->priv;
1128
1129
	if (!priv->cache_dir) {
1130
		/* Set a default cache dir, different for each user */
1131
		priv->cache_dir = g_build_filename (g_get_user_cache_dir (),
1132
						    "httpcache",
1133
						    NULL);
1134
		if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
1135
			g_mkdir_with_parents (priv->cache_dir, 0700);
1136
	}
1137
1138
	if (G_OBJECT_CLASS (webkit_soup_cache_parent_class)->constructed)
1139
		G_OBJECT_CLASS (webkit_soup_cache_parent_class)->constructed (object);
1140
}
1141
1142
#define WEBKIT_SOUP_CACHE_TYPE_TYPE (webkit_soup_cache_type_get_type ())
1143
static GType
1144
webkit_soup_cache_type_get_type (void)
1145
{
1146
	static GType cache_type = 0;
1147
1148
	static const GEnumValue cache_types[] = {
1149
		{ WEBKIT_SOUP_CACHE_SINGLE_USER, "Single user cache", "user" },
1150
		{ WEBKIT_SOUP_CACHE_SHARED, "Shared cache", "shared" },
1151
		{ 0, NULL, NULL }
1152
	};
1153
1154
	if (!cache_type) {
1155
		cache_type = g_enum_register_static ("WebKitSoupCacheType", cache_types);
1156
	}
1157
	return cache_type;
1158
}
1159
1160
static void
1161
webkit_soup_cache_class_init (WebKitSoupCacheClass *cache_class)
1162
{
1163
	GObjectClass *gobject_class = (GObjectClass *)cache_class;
1164
1165
	gobject_class->finalize = webkit_soup_cache_finalize;
1166
	gobject_class->constructed = webkit_soup_cache_constructed;
1167
	gobject_class->set_property = webkit_soup_cache_set_property;
1168
	gobject_class->get_property = webkit_soup_cache_get_property;
1169
1170
	cache_class->get_cacheability = get_cacheability;
1171
1172
	g_object_class_install_property (gobject_class, PROP_CACHE_DIR,
1173
					 g_param_spec_string ("cache-dir",
1174
							      "Cache directory",
1175
							      "The directory to store the cache files",
1176
							      NULL,
1177
							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1178
1179
	g_object_class_install_property (gobject_class, PROP_CACHE_TYPE,
1180
					 g_param_spec_enum ("cache-type",
1181
							    "Cache type",
1182
							    "Whether the cache is private or shared",
1183
							    WEBKIT_SOUP_CACHE_TYPE_TYPE,
1184
							    WEBKIT_SOUP_CACHE_SINGLE_USER,
1185
							    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1186
1187
	g_type_class_add_private (cache_class, sizeof (WebKitSoupCachePrivate));
1188
}
1189
1190
/**
1191
 * webkit_soup_cache_new:
1192
 * @cache_dir: the directory to store the cached data, or %NULL to use the default one
1193
 * @cache_type: the #WebKitSoupCacheType of the cache
1194
 *
1195
 * Creates a new #WebKitSoupCache.
1196
 *
1197
 * Returns: a new #WebKitSoupCache
1198
 *
1199
 * Since: 2.28
1200
 **/
1201
WebKitSoupCache *
1202
webkit_soup_cache_new (const char *cache_dir, WebKitSoupCacheType cache_type)
1203
{
1204
	return g_object_new (WEBKIT_TYPE_SOUP_CACHE,
1205
			     "cache-dir", cache_dir,
1206
			     "cache-type", cache_type,
1207
			     NULL);
1208
}
1209
1210
/**
1211
 * webkit_soup_cache_has_response:
1212
 * @cache: a #WebKitSoupCache
1213
 * @msg: a #SoupMessage
1214
 *
1215
 * This function calculates whether the @cache object has a proper
1216
 * response for the request @msg given the flags both in the request
1217
 * and the cached reply and the time ellapsed since it was cached.
1218
 *
1219
 * Returns: whether or not the @cache has a valid response for @msg
1220
 **/
1221
WebKitSoupCacheResponse
1222
webkit_soup_cache_has_response (WebKitSoupCache *cache, SoupMessage *msg)
1223
{
1224
	char *key;
1225
	WebKitSoupCacheEntry *entry;
1226
	const char *cache_control;
1227
	GHashTable *hash;
1228
	gpointer value;
1229
	gboolean must_revalidate;
1230
	int max_age, max_stale, min_fresh;
1231
1232
	key = soup_message_get_cache_key (msg);
1233
	entry = g_hash_table_lookup (cache->priv->cache, key);
1234
1235
	/* 1. The presented Request-URI and that of stored response
1236
	 * match
1237
	 */
1238
	if (!entry)
1239
		return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
1240
1241
	/* Increase hit count */
1242
	entry->hits++;
1243
1244
	if (entry->dirty || entry->being_validated)
1245
		return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
1246
1247
	/* 2. The request method associated with the stored response
1248
	 *  allows it to be used for the presented request
1249
	 */
1250
1251
	/* In practice this means we only return our resource for GET,
1252
	 * cacheability for other methods is a TODO in the RFC
1253
	 * (TODO: although we could return the headers for HEAD
1254
	 * probably).
1255
	 */
1256
	if (msg->method != SOUP_METHOD_GET)
1257
		return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
1258
1259
	/* 3. Selecting request-headers nominated by the stored
1260
	 * response (if any) match those presented.
1261
	 */
1262
1263
	/* TODO */
1264
1265
	/* 4. The presented request and stored response are free from
1266
	 * directives that would prevent its use.
1267
	 */
1268
1269
	must_revalidate = FALSE;
1270
	max_age = max_stale = min_fresh = -1;
1271
1272
	cache_control = soup_message_headers_get (msg->request_headers, "Cache-Control");
1273
	if (cache_control) {
1274
		hash = soup_header_parse_param_list (cache_control);
1275
1276
		if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) {
1277
			g_hash_table_destroy (hash);
1278
			return WEBKIT_SOUP_CACHE_RESPONSE_STALE;
1279
		}
1280
1281
		if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) {
1282
			entry->must_revalidate = TRUE;
1283
		}
1284
1285
		if (g_hash_table_lookup_extended (hash, "max-age", NULL, &value)) {
1286
			max_age = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
1287
		}
1288
1289
		/* max-stale can have no value set, we need to use _extended */
1290
		if (g_hash_table_lookup_extended (hash, "max-stale", NULL, &value)) {
1291
			if (value)
1292
				max_stale = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
1293
			else
1294
				max_stale = G_MAXINT32;
1295
		}
1296
1297
		value = g_hash_table_lookup (hash, "min-fresh");
1298
		if (value)
1299
			min_fresh = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
1300
1301
		g_hash_table_destroy (hash);
1302
1303
		if (max_age != -1) {
1304
			guint current_age = webkit_soup_cache_entry_get_current_age (entry);
1305
1306
			/* If we are over max-age and max-stale is not
1307
			   set, do not use the value from the cache
1308
			   without validation */
1309
			if (max_age <= current_age && max_stale == -1)
1310
				return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
1311
		}
1312
	}
1313
1314
	/* 5. The stored response is either: fresh, allowed to be
1315
	 * served stale or succesfully validated
1316
	 */
1317
	/* TODO consider also proxy-revalidate & s-maxage */
1318
	if (entry->must_revalidate)
1319
		return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
1320
1321
	if (!webkit_soup_cache_entry_is_fresh_enough (entry, min_fresh)) {
1322
		/* Not fresh, can it be served stale? */
1323
		if (max_stale != -1) {
1324
			/* G_MAXINT32 means we accept any staleness */
1325
			if (max_stale == G_MAXINT32)
1326
				return WEBKIT_SOUP_CACHE_RESPONSE_FRESH;
1327
1328
			if ((webkit_soup_cache_entry_get_current_age (entry) - entry->freshness_lifetime) <= max_stale)
1329
				return WEBKIT_SOUP_CACHE_RESPONSE_FRESH;
1330
		}
1331
1332
		return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
1333
	}
1334
1335
	return WEBKIT_SOUP_CACHE_RESPONSE_FRESH;
1336
}
1337
1338
/**
1339
 * webkit_soup_cache_get_cacheability:
1340
 * @cache: a #WebKitSoupCache
1341
 * @msg: a #SoupMessage
1342
 *
1343
 * Calculates whether the @msg can be cached or not.
1344
 *
1345
 * Returns: a #WebKitSoupCacheability value indicating whether the @msg can be cached or not.
1346
 **/
1347
WebKitSoupCacheability
1348
webkit_soup_cache_get_cacheability (WebKitSoupCache *cache, SoupMessage *msg)
1349
{
1350
	g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), WEBKIT_SOUP_CACHE_UNCACHEABLE);
1351
	g_return_val_if_fail (SOUP_IS_MESSAGE (msg), WEBKIT_SOUP_CACHE_UNCACHEABLE);
1352
1353
	return WEBKIT_SOUP_CACHE_GET_CLASS (cache)->get_cacheability (cache, msg);
1354
}
1355
1356
static gboolean
1357
force_flush_timeout (gpointer data)
1358
{
1359
	gboolean *forced = (gboolean *)data;
1360
	*forced = TRUE;
1361
1362
	return FALSE;
1363
}
1364
1365
/**
1366
 * webkit_soup_cache_flush:
1367
 * @cache: a #WebKitSoupCache
1368
 * @session: the #SoupSession associated with the @cache
1369
 *
1370
 * This function will force all pending writes in the @cache to be
1371
 * committed to disk. For doing so it will iterate the #GMainContext
1372
 * associated with the @session (which can be the default one) as long
1373
 * as needed.
1374
 **/
1375
void
1376
webkit_soup_cache_flush (WebKitSoupCache *cache)
1377
{
1378
	GMainContext *async_context;
1379
	SoupSession *session;
1380
	guint timeout_id;
1381
	gboolean forced = FALSE;
1382
1383
	g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache));
1384
1385
	session = cache->priv->session;
1386
	g_return_if_fail (SOUP_IS_SESSION (session));
1387
	async_context = soup_session_get_async_context (session);
1388
1389
	/* We give cache 10 secs to finish */
1390
	timeout_id = g_timeout_add (10000, force_flush_timeout, &forced);
1391
1392
	while (!forced && cache->priv->n_pending > 0)
1393
		g_main_context_iteration (async_context, FALSE);
1394
1395
	if (!forced)
1396
		g_source_remove (timeout_id);
1397
	else
1398
		g_warning ("Cache flush finished despite %d pending requests", cache->priv->n_pending);
1399
}
1400
1401
static void
1402
remove_cache_item (gpointer key,
1403
		   gpointer value,
1404
		   WebKitSoupCache *cache)
1405
{
1406
	WebKitSoupCacheEntry *entry = g_hash_table_lookup (cache->priv->cache, (const gchar *)key);
1407
	webkit_soup_cache_entry_delete (cache, entry);
1408
}
1409
1410
/**
1411
 * webkit_soup_cache_clear:
1412
 * @cache: a #WebKitSoupCache
1413
 *
1414
 * Will remove all entries in the @cache plus all the cache files
1415
 * associated with them.
1416
 **/
1417
void
1418
webkit_soup_cache_clear (WebKitSoupCache *cache)
1419
{
1420
	GHashTable *hash;
1421
1422
	g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache));
1423
1424
	hash = cache->priv->cache;
1425
	g_return_if_fail (hash);
1426
1427
	g_hash_table_foreach (hash, (GHFunc)remove_cache_item, cache);
1428
}
1429
1430
SoupMessage *
1431
webkit_soup_cache_generate_conditional_request (WebKitSoupCache *cache, SoupMessage *original)
1432
{
1433
	SoupMessage *msg;
1434
	SoupURI *uri;
1435
	WebKitSoupCacheEntry *entry;
1436
	char *key;
1437
	const char *value;
1438
1439
	g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), NULL);
1440
	g_return_val_if_fail (SOUP_IS_MESSAGE (original), NULL);
1441
1442
	/* First copy the data we need from the original message */
1443
	uri = soup_message_get_uri (original);
1444
	msg = soup_message_new_from_uri (original->method, uri);
1445
1446
	soup_message_headers_foreach (original->request_headers,
1447
				      (SoupMessageHeadersForeachFunc)copy_headers,
1448
				      msg->request_headers);
1449
1450
	/* Now add the validator entries in the header from the cached
1451
	   data */
1452
	key = soup_message_get_cache_key (original);
1453
	entry = g_hash_table_lookup (cache->priv->cache, key);
1454
	g_free (key);
1455
1456
	g_return_val_if_fail (entry, NULL);
1457
1458
	entry->being_validated = TRUE;
1459
1460
	value = soup_message_headers_get (entry->headers, "Last-Modified");
1461
	if (value)
1462
		soup_message_headers_append (msg->request_headers,
1463
					     "If-Modified-Since",
1464
					     value);
1465
	value = soup_message_headers_get (entry->headers, "ETag");
1466
	if (value)
1467
		soup_message_headers_append (msg->request_headers,
1468
					     "If-None-Match",
1469
					     value);
1470
	return msg;
1471
}
1472
1473
#define WEBKIT_SOUP_CACHE_FILE "soup.cache"
1474
1475
#define WEBKIT_SOUP_CACHE_HEADERS_FORMAT "{ss}"
1476
#define WEBKIT_SOUP_CACHE_PHEADERS_FORMAT "(ssbuuuuua" WEBKIT_SOUP_CACHE_HEADERS_FORMAT ")"
1477
#define WEBKIT_SOUP_CACHE_ENTRIES_FORMAT "a" WEBKIT_SOUP_CACHE_PHEADERS_FORMAT
1478
1479
/* Basically the same format than above except that some strings are
1480
   prepended with &. This way the GVariant returns a pointer to the
1481
   data instead of duplicating the string */
1482
#define WEBKIT_SOUP_CACHE_DECODE_HEADERS_FORMAT "{&s&s}"
1483
1484
static void
1485
pack_entry (gpointer key,
1486
	    gpointer value,
1487
	    gpointer data)
1488
{
1489
	WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *)value;
1490
	SoupMessageHeadersIter iter;
1491
	const gchar *header_key, *header_value;
1492
	GVariantBuilder *headers_builder;
1493
	GVariantBuilder *entries_builder = (GVariantBuilder *)data;
1494
1495
	/* Do not store non-consolidated entries */
1496
	if (entry->dirty || entry->writing || !entry->key)
1497
		return;
1498
1499
	/* Pack headers */
1500
	headers_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
1501
	soup_message_headers_iter_init (&iter, entry->headers);
1502
	while (soup_message_headers_iter_next (&iter, &header_key, &header_value))
1503
		g_variant_builder_add (headers_builder, WEBKIT_SOUP_CACHE_HEADERS_FORMAT, header_key, header_value);
1504
1505
	/* Entry data */
1506
	g_variant_builder_add (entries_builder, WEBKIT_SOUP_CACHE_PHEADERS_FORMAT,
1507
			       entry->key, entry->filename, entry->must_revalidate,
1508
			       entry->freshness_lifetime, entry->corrected_initial_age,
1509
			       entry->response_time, entry->hits, entry->length, headers_builder);
1510
1511
	g_variant_builder_unref (headers_builder);
1512
}
1513
1514
void
1515
webkit_soup_cache_dump (WebKitSoupCache *cache)
1516
{
1517
	WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache);
1518
	gchar *filename;
1519
	GVariantBuilder *entries_builder;
1520
	GVariant *cache_variant;
1521
1522
	/* Create the builder and iterate over all entries */
1523
	entries_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
1524
	g_hash_table_foreach (cache->priv->cache, (GHFunc)pack_entry, entries_builder);
1525
1526
	/* Serialize and dump */
1527
	cache_variant = g_variant_new (WEBKIT_SOUP_CACHE_ENTRIES_FORMAT, entries_builder);
1528
	g_variant_builder_unref (entries_builder);
1529
1530
	filename = g_build_filename (priv->cache_dir, WEBKIT_SOUP_CACHE_FILE, NULL);
1531
	g_file_set_contents (filename, (const gchar *)g_variant_get_data (cache_variant),
1532
			     g_variant_get_size (cache_variant), NULL);
1533
	g_free (filename);
1534
	g_variant_unref (cache_variant);
1535
}
1536
1537
void
1538
webkit_soup_cache_load (WebKitSoupCache *cache)
1539
{
1540
	gchar *filename = NULL, *contents = NULL;
1541
	GVariant *cache_variant;
1542
	GVariantIter *entries_iter, *headers_iter;
1543
	GVariantType *variant_format;
1544
	gsize length;
1545
	WebKitSoupCacheEntry *entry;
1546
	WebKitSoupCachePrivate *priv = cache->priv;
1547
1548
	filename = g_build_filename (priv->cache_dir, WEBKIT_SOUP_CACHE_FILE, NULL);
1549
	if (!g_file_get_contents (filename, &contents, &length, NULL)) {
1550
		g_free (filename);
1551
		return;
1552
	}
1553
	g_free (filename);
1554
1555
	variant_format = g_variant_type_new (WEBKIT_SOUP_CACHE_ENTRIES_FORMAT);
1556
	cache_variant = g_variant_new_from_data (variant_format, (const gchar *)contents, length, FALSE, NULL, NULL);
1557
	g_variant_type_free (variant_format);
1558
1559
	g_variant_get (cache_variant, WEBKIT_SOUP_CACHE_ENTRIES_FORMAT, &entries_iter);
1560
	entry = g_slice_new0 (WebKitSoupCacheEntry);
1561
1562
	while (g_variant_iter_loop (entries_iter, WEBKIT_SOUP_CACHE_PHEADERS_FORMAT,
1563
				    &entry->key, &entry->filename, &entry->must_revalidate,
1564
				    &entry->freshness_lifetime, &entry->corrected_initial_age,
1565
				    &entry->response_time, &entry->hits, &entry->length,
1566
                                    &headers_iter)) {
1567
		const gchar *header_key, *header_value;
1568
1569
		/* SoupMessage Headers */
1570
		entry->headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
1571
		while (g_variant_iter_loop (headers_iter, WEBKIT_SOUP_CACHE_DECODE_HEADERS_FORMAT, &header_key, &header_value))
1572
			soup_message_headers_append (entry->headers, header_key, header_value);
1573
1574
		/* Insert in cache */
1575
		if (!webkit_soup_cache_entry_insert_by_key (cache, (const gchar *)entry->key, entry))
1576
			webkit_soup_cache_entry_free (entry);
1577
1578
		/* New entry for the next iteration. This creates an
1579
		   extra object the last iteration but it's worth it
1580
		   as we save several if's */
1581
		entry = g_slice_new0 (WebKitSoupCacheEntry);
1582
	}
1583
	/* Remove last created entry */
1584
	g_slice_free (WebKitSoupCacheEntry, entry);
1585
1586
	/* frees */
1587
	g_variant_iter_free (entries_iter);
1588
	g_variant_unref (cache_variant);
1589
}
1590
1591
void
1592
webkit_soup_cache_set_max_size (WebKitSoupCache *cache,
1593
				guint max_size)
1594
{
1595
	cache->priv->max_size = max_size;
1596
	cache->priv->max_entry_data_size = cache->priv->max_size / MAX_ENTRY_DATA_PERCENTAGE;
1597
}
1598
1599
guint
1600
webkit_soup_cache_get_max_size (WebKitSoupCache *cache)
1601
{
1602
	return cache->priv->max_size;
1603
}
- 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-05  Peter Kasting  <pkasting@google.com>
10
2010-09-05  Peter Kasting  <pkasting@google.com>
2
11
3
        Reviewed by Adam Barth.
12
        Reviewed by Adam Barth.
- 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