2010-10-13 Sergio Villar Senin <svillar@igalia.com>
[WebKit-https.git] / WebCore / platform / network / soup / cache / soup-request-http.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-request-http.c: http: URI request object
4  *
5  * Copyright (C) 2009 Red Hat, Inc.
6  * Copyright (C) 2010 Igalia, S.L.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <glib/gi18n.h>
29
30 #include "soup-cache.h"
31 #include "soup-cache-private.h"
32 #include "soup-http-input-stream.h"
33 #include "soup-request-http.h"
34
35 G_DEFINE_TYPE (WebKitSoupRequestHTTP, webkit_soup_request_http, WEBKIT_TYPE_SOUP_REQUEST)
36
37 struct _WebKitSoupRequestHTTPPrivate {
38         SoupMessage *msg;
39 };
40
41 /**
42  * webkit_soup_request_http_get_message:
43  * @http: a #WebKitSoupRequestHTTP object
44  *
45  * Gets a new reference to the #SoupMessage associated to this SoupRequest
46  *
47  * Returns: a new reference to the #SoupMessage
48  **/
49 SoupMessage *
50 webkit_soup_request_http_get_message (WebKitSoupRequestHTTP *http)
51 {
52         g_return_val_if_fail (WEBKIT_IS_SOUP_REQUEST_HTTP (http), NULL);
53
54         return g_object_ref (http->priv->msg);
55 }
56
57 static void
58 webkit_soup_request_http_init (WebKitSoupRequestHTTP *http)
59 {
60         http->priv = G_TYPE_INSTANCE_GET_PRIVATE (http, WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPPrivate);
61 }
62
63 static gboolean
64 webkit_soup_request_http_check_uri (WebKitSoupRequest  *request,
65                                     SoupURI      *uri,
66                                     GError      **error)
67 {
68         WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
69
70         if (!SOUP_URI_VALID_FOR_HTTP (uri))
71                 return FALSE;
72
73         http->priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
74         return TRUE;
75 }
76
77 static void
78 webkit_soup_request_http_finalize (GObject *object)
79 {
80         WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (object);
81
82         if (http->priv->msg)
83                 g_object_unref (http->priv->msg);
84
85         G_OBJECT_CLASS (webkit_soup_request_http_parent_class)->finalize (object);
86 }
87
88 static GInputStream *
89 webkit_soup_request_http_send (WebKitSoupRequest          *request,
90                                GCancellable         *cancellable,
91                                GError              **error)
92 {
93         WebKitSoupHTTPInputStream *httpstream;
94         WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
95
96         httpstream = webkit_soup_http_input_stream_new (webkit_soup_request_get_session (request), http->priv->msg);
97         if (!webkit_soup_http_input_stream_send (httpstream, cancellable, error)) {
98                 g_object_unref (httpstream);
99                 return NULL;
100         }
101         return (GInputStream *)httpstream;
102 }
103
104
105 static void
106 sent_async (GObject *source, GAsyncResult *result, gpointer user_data)
107 {
108         WebKitSoupHTTPInputStream *httpstream = WEBKIT_SOUP_HTTP_INPUT_STREAM (source);
109         GSimpleAsyncResult *simple = user_data;
110         GError *error = NULL;
111
112         if (webkit_soup_http_input_stream_send_finish (httpstream, result, &error)) {
113                 g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
114         } else {
115                 g_simple_async_result_set_from_error (simple, error);
116                 g_error_free (error);
117                 g_object_unref (httpstream);
118         }
119         g_simple_async_result_complete (simple);
120         g_object_unref (simple);
121 }
122
123
124 typedef struct {
125         WebKitSoupRequestHTTP *req;
126         SoupMessage *original;
127         GCancellable *cancellable;
128         GAsyncReadyCallback callback;
129         gpointer user_data;
130 } ConditionalHelper;
131
132
133 static void
134 conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
135 {
136         ConditionalHelper *helper = (ConditionalHelper *)user_data;
137         GSimpleAsyncResult *simple;
138         WebKitSoupHTTPInputStream *httpstream;
139
140         simple = g_simple_async_result_new (G_OBJECT (helper->req),
141                                             helper->callback, helper->user_data,
142                                             conditional_get_ready_cb);
143
144         if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
145                 WebKitSoupCache *cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
146
147                 httpstream = (WebKitSoupHTTPInputStream *)webkit_soup_cache_send_response (cache, msg);
148                 if (httpstream) {
149                         const gchar *content_type;
150
151                         g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
152
153                         soup_message_got_headers (helper->original);
154                         content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
155                         soup_message_content_sniffed (helper->original, content_type, NULL);
156
157                         g_simple_async_result_complete (simple);
158
159                         soup_message_finished (helper->original);
160
161                         g_object_unref (simple);
162                 } else {
163                         /* Ask again for the resource, somehow the cache cannot locate it */
164                         httpstream = webkit_soup_http_input_stream_new (session, helper->original);
165                         webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
166                                                                   helper->cancellable, sent_async, simple);
167                 }
168         } else {
169                 /* It is in the cache but it was modified remotely */
170                 httpstream = webkit_soup_http_input_stream_new (session, helper->original);
171                 webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
172                                                           helper->cancellable, sent_async, simple);
173         }
174
175         g_object_unref (helper->req);
176         g_object_unref (helper->original);
177         g_slice_free (ConditionalHelper, helper);
178 }
179
180 typedef struct {
181         WebKitSoupRequestHTTP *http;
182         GAsyncReadyCallback callback;
183         gpointer user_data;
184 } SendAsyncHelper;
185
186 static void webkit_soup_request_http_send_async (WebKitSoupRequest          *request,
187                                                  GCancellable         *cancellable,
188                                                  GAsyncReadyCallback callback,
189                                                  gpointer user_data);
190
191 static gboolean
192 send_async_cb (gpointer data)
193 {
194         GSimpleAsyncResult *simple;
195         WebKitSoupHTTPInputStream *httpstream;
196         SoupSession *session;
197         WebKitSoupCache *cache;
198         SendAsyncHelper *helper = (SendAsyncHelper *)data;
199
200         session = webkit_soup_request_get_session (WEBKIT_SOUP_REQUEST (helper->http));
201         cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
202
203         httpstream = (WebKitSoupHTTPInputStream *)webkit_soup_cache_send_response (cache, SOUP_MESSAGE (helper->http->priv->msg));
204
205         if (httpstream) {
206                 const gchar *content_type;
207
208                 simple = g_simple_async_result_new (G_OBJECT (helper->http),
209                                                     helper->callback, helper->user_data,
210                                                     webkit_soup_request_http_send_async);
211                 g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
212
213                 /* Update message status */
214                 soup_message_set_status (helper->http->priv->msg, SOUP_STATUS_OK);
215
216                 /* Issue signals  */
217                 soup_message_got_headers (helper->http->priv->msg);
218                 content_type = soup_message_headers_get_content_type (helper->http->priv->msg->response_headers, NULL);
219                 soup_message_content_sniffed (helper->http->priv->msg, content_type, NULL);
220
221                 g_simple_async_result_complete (simple);
222
223                 soup_message_finished (helper->http->priv->msg);
224
225                 g_object_unref (simple);
226         }
227
228         g_slice_free (SendAsyncHelper, helper);
229
230         return FALSE;
231 }
232
233 static void
234 webkit_soup_request_http_send_async (WebKitSoupRequest          *request,
235                                      GCancellable         *cancellable,
236                                      GAsyncReadyCallback callback,
237                                      gpointer user_data)
238 {
239         WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
240         WebKitSoupHTTPInputStream *httpstream;
241         GSimpleAsyncResult *simple;
242         SoupSession *session;
243         WebKitSoupCache *cache;
244
245         session = webkit_soup_request_get_session (request);
246         cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
247
248         if (cache) {
249                 WebKitSoupCacheResponse response;
250
251                 response = webkit_soup_cache_has_response (cache, http->priv->msg);
252                 if (response == WEBKIT_SOUP_CACHE_RESPONSE_FRESH) {
253                         /* Do return the stream asynchronously as in
254                            the other cases. It's not enough to use
255                            g_simple_async_result_complete_in_idle as
256                            the signals must be also emitted
257                            asynchronously */
258                         SendAsyncHelper *helper = g_slice_new (SendAsyncHelper);
259                         helper->http = http;
260                         helper->callback = callback;
261                         helper->user_data = user_data;
262                         g_timeout_add (0, send_async_cb, helper);
263                         return;
264                 } else if (response == WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
265                         SoupMessage *conditional_msg;
266                         ConditionalHelper *helper;
267
268                         conditional_msg = webkit_soup_cache_generate_conditional_request (cache, http->priv->msg);
269
270                         helper = g_slice_new0 (ConditionalHelper);
271                         helper->req = g_object_ref (http);
272                         helper->original = g_object_ref (http->priv->msg);
273                         helper->cancellable = cancellable;
274                         helper->callback = callback;
275                         helper->user_data = user_data;
276                         soup_session_queue_message (session, conditional_msg,
277                                                     conditional_get_ready_cb,
278                                                     helper);
279                         return;
280                 }
281         }
282
283         simple = g_simple_async_result_new (G_OBJECT (http),
284                                             callback, user_data,
285                                             webkit_soup_request_http_send_async);
286         httpstream = webkit_soup_http_input_stream_new (webkit_soup_request_get_session (request),
287                                                         http->priv->msg);
288         webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
289                                                   cancellable, sent_async, simple);
290 }
291
292 static GInputStream *
293 webkit_soup_request_http_send_finish (WebKitSoupRequest          *request,
294                                       GAsyncResult         *result,
295                                       GError              **error)
296 {
297         GSimpleAsyncResult *simple;
298
299         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), webkit_soup_request_http_send_async) || g_simple_async_result_is_valid (result, G_OBJECT (request), conditional_get_ready_cb), NULL);
300
301         simple = G_SIMPLE_ASYNC_RESULT (result);
302         if (g_simple_async_result_propagate_error (simple, error))
303                 return NULL;
304         return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
305 }
306
307 static goffset
308 webkit_soup_request_http_get_content_length (WebKitSoupRequest *request)
309 {
310         WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
311
312         return soup_message_headers_get_content_length (http->priv->msg->response_headers);
313 }
314
315 static const char *
316 webkit_soup_request_http_get_content_type (WebKitSoupRequest *request)
317 {
318         WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
319
320         return soup_message_headers_get_content_type (http->priv->msg->response_headers, NULL);
321 }
322
323 static void
324 webkit_soup_request_http_class_init (WebKitSoupRequestHTTPClass *request_http_class)
325 {
326         GObjectClass *object_class = G_OBJECT_CLASS (request_http_class);
327         WebKitSoupRequestClass *request_class =
328                 WEBKIT_SOUP_REQUEST_CLASS (request_http_class);
329
330         g_type_class_add_private (request_http_class, sizeof (WebKitSoupRequestHTTPPrivate));
331
332         object_class->finalize = webkit_soup_request_http_finalize;
333
334         request_class->check_uri = webkit_soup_request_http_check_uri;
335         request_class->send = webkit_soup_request_http_send;
336         request_class->send_async = webkit_soup_request_http_send_async;
337         request_class->send_finish = webkit_soup_request_http_send_finish;
338         request_class->get_content_length = webkit_soup_request_http_get_content_length;
339         request_class->get_content_type = webkit_soup_request_http_get_content_type;
340 }