Git init
[profile/ivi/libsoup2.4.git] / libsoup / 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, 2010 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 #define LIBSOUP_USE_UNSTABLE_REQUEST_API
31
32 #include "soup-request-http.h"
33 #include "soup-cache.h"
34 #include "soup-cache-private.h"
35 #include "soup-content-sniffer.h"
36 #include "soup-http-input-stream.h"
37 #include "soup-message.h"
38 #include "soup-session.h"
39 #include "soup-uri.h"
40
41 G_DEFINE_TYPE (SoupRequestHTTP, soup_request_http, SOUP_TYPE_REQUEST)
42
43 struct _SoupRequestHTTPPrivate {
44         SoupMessage *msg;
45 };
46
47 static void
48 soup_request_http_init (SoupRequestHTTP *http)
49 {
50         http->priv = G_TYPE_INSTANCE_GET_PRIVATE (http, SOUP_TYPE_REQUEST_HTTP, SoupRequestHTTPPrivate);
51 }
52
53 static gboolean
54 soup_request_http_check_uri (SoupRequest  *request,
55                              SoupURI      *uri,
56                              GError      **error)
57 {
58         SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
59
60         if (!SOUP_URI_VALID_FOR_HTTP (uri))
61                 return FALSE;
62
63         http->priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
64         return TRUE;
65 }
66
67 static void
68 soup_request_http_finalize (GObject *object)
69 {
70         SoupRequestHTTP *http = SOUP_REQUEST_HTTP (object);
71
72         if (http->priv->msg)
73                 g_object_unref (http->priv->msg);
74
75         G_OBJECT_CLASS (soup_request_http_parent_class)->finalize (object);
76 }
77
78 static GInputStream *
79 soup_request_http_send (SoupRequest          *request,
80                         GCancellable         *cancellable,
81                         GError              **error)
82 {
83         SoupHTTPInputStream *httpstream;
84         SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
85
86         httpstream = soup_http_input_stream_new (soup_request_get_session (request), http->priv->msg);
87         if (!soup_http_input_stream_send (httpstream, cancellable, error)) {
88                 g_object_unref (httpstream);
89                 return NULL;
90         }
91         return (GInputStream *)httpstream;
92 }
93
94
95 static void
96 sent_async (GObject *source, GAsyncResult *result, gpointer user_data)
97 {
98         SoupHTTPInputStream *httpstream = SOUP_HTTP_INPUT_STREAM (source);
99         GSimpleAsyncResult *simple = user_data;
100         GError *error = NULL;
101
102         if (soup_http_input_stream_send_finish (httpstream, result, &error)) {
103                 g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
104         } else {
105                 g_simple_async_result_set_from_error (simple, error);
106                 g_error_free (error);
107                 g_object_unref (httpstream);
108         }
109         g_simple_async_result_complete (simple);
110         g_object_unref (simple);
111 }
112
113
114 typedef struct {
115         SoupRequestHTTP *req;
116         SoupMessage *original;
117         GCancellable *cancellable;
118         GAsyncReadyCallback callback;
119         gpointer user_data;
120 } ConditionalHelper;
121
122 static void
123 conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
124 {
125         ConditionalHelper *helper = (ConditionalHelper *)user_data;
126         GSimpleAsyncResult *simple;
127         SoupHTTPInputStream *httpstream;
128
129         simple = g_simple_async_result_new (G_OBJECT (helper->req),
130                                             helper->callback, helper->user_data,
131                                             conditional_get_ready_cb);
132
133         if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
134                 SoupCache *cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
135
136                 httpstream = (SoupHTTPInputStream *)soup_cache_send_response (cache, helper->original);
137                 if (httpstream) {
138                         g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
139
140                         soup_message_got_headers (helper->original);
141
142                         if (soup_session_get_feature_for_message (session, SOUP_TYPE_CONTENT_SNIFFER, helper->original)) {
143                                 const char *content_type =
144                                         soup_message_headers_get_content_type (helper->original->response_headers, NULL);
145                                 soup_message_content_sniffed (helper->original, content_type, NULL);
146                         }
147
148                         g_simple_async_result_complete (simple);
149
150                         soup_message_finished (helper->original);
151
152                         g_object_unref (simple);
153                 } else {
154                         /* Ask again for the resource, somehow the cache cannot locate it */
155                         httpstream = soup_http_input_stream_new (session, helper->original);
156                         soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
157                                                            helper->cancellable, sent_async, simple);
158                 }
159         } else {
160                 /* It is in the cache but it was modified remotely */
161                 httpstream = soup_http_input_stream_new (session, helper->original);
162                 soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
163                                                    helper->cancellable, sent_async, simple);
164         }
165
166         g_object_unref (helper->req);
167         g_object_unref (helper->original);
168         g_slice_free (ConditionalHelper, helper);
169 }
170
171 typedef struct {
172         SoupRequestHTTP *http;
173         GAsyncReadyCallback callback;
174         gpointer user_data;
175         SoupHTTPInputStream *httpstream;
176 } SendAsyncHelper;
177
178 static void soup_request_http_send_async (SoupRequest          *request,
179                                           GCancellable         *cancellable,
180                                           GAsyncReadyCallback callback,
181                                           gpointer user_data);
182
183 static gboolean
184 send_async_cb (gpointer data)
185 {
186         GSimpleAsyncResult *simple;
187         SoupSession *session;
188         SendAsyncHelper *helper = (SendAsyncHelper *)data;
189
190         session = soup_request_get_session (SOUP_REQUEST (helper->http));
191         simple = g_simple_async_result_new (G_OBJECT (helper->http),
192                                             helper->callback, helper->user_data,
193                                             soup_request_http_send_async);
194
195         g_simple_async_result_set_op_res_gpointer (simple, helper->httpstream, g_object_unref);
196
197         /* Issue signals  */
198         soup_message_got_headers (helper->http->priv->msg);
199
200         if (soup_session_get_feature_for_message (session, SOUP_TYPE_CONTENT_SNIFFER, helper->http->priv->msg)) {
201                 const char *content_type = soup_message_headers_get_content_type (helper->http->priv->msg->response_headers, NULL);
202                 soup_message_content_sniffed (helper->http->priv->msg, content_type, NULL);
203         }
204
205         g_simple_async_result_complete (simple);
206
207         soup_message_finished (helper->http->priv->msg);
208
209         g_object_unref (simple);
210
211         g_object_unref (helper->http);
212         g_slice_free (SendAsyncHelper, helper);
213
214         return FALSE;
215 }
216
217 static void
218 soup_request_http_send_async (SoupRequest          *request,
219                               GCancellable         *cancellable,
220                               GAsyncReadyCallback   callback,
221                               gpointer              user_data)
222 {
223         SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
224         SoupHTTPInputStream *httpstream;
225         GSimpleAsyncResult *simple;
226         SoupSession *session;
227         SoupCache *cache;
228
229         session = soup_request_get_session (request);
230         cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
231
232         if (cache) {
233                 SoupCacheResponse response;
234
235                 response = soup_cache_has_response (cache, http->priv->msg);
236                 if (response == SOUP_CACHE_RESPONSE_FRESH) {
237                         SoupHTTPInputStream *httpstream;
238
239                         httpstream = (SoupHTTPInputStream *)
240                                 soup_cache_send_response (cache, http->priv->msg);
241
242                         /* Cached resource file could have been deleted outside
243                          */
244                         if (httpstream) {
245                                 /* Do return the stream asynchronously as in
246                                  * the other cases. It's not enough to use
247                                  * g_simple_async_result_complete_in_idle as
248                                  * the signals must be also emitted
249                                  * asynchronously
250                                  */
251                                 SendAsyncHelper *helper = g_slice_new (SendAsyncHelper);
252                                 helper->http = g_object_ref (http);
253                                 helper->callback = callback;
254                                 helper->user_data = user_data;
255                                 helper->httpstream = httpstream;
256                                 soup_add_timeout (soup_session_get_async_context (session),
257                                                   0, send_async_cb, helper);
258                                 return;
259                         }
260                 } else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
261                         SoupMessage *conditional_msg;
262                         ConditionalHelper *helper;
263
264                         conditional_msg = soup_cache_generate_conditional_request (cache, http->priv->msg);
265
266                         helper = g_slice_new0 (ConditionalHelper);
267                         helper->req = g_object_ref (http);
268                         helper->original = g_object_ref (http->priv->msg);
269                         helper->cancellable = cancellable;
270                         helper->callback = callback;
271                         helper->user_data = user_data;
272                         soup_session_queue_message (session, conditional_msg,
273                                                     conditional_get_ready_cb,
274                                                     helper);
275                         return;
276                 }
277         }
278
279         simple = g_simple_async_result_new (G_OBJECT (http),
280                                             callback, user_data,
281                                             soup_request_http_send_async);
282         httpstream = soup_http_input_stream_new (soup_request_get_session (request),
283                                                         http->priv->msg);
284         soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
285                                            cancellable, sent_async, simple);
286 }
287
288 static GInputStream *
289 soup_request_http_send_finish (SoupRequest          *request,
290                                GAsyncResult         *result,
291                                GError              **error)
292 {
293         GSimpleAsyncResult *simple;
294
295         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);
296
297         simple = G_SIMPLE_ASYNC_RESULT (result);
298         if (g_simple_async_result_propagate_error (simple, error))
299                 return NULL;
300         return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
301 }
302
303 static goffset
304 soup_request_http_get_content_length (SoupRequest *request)
305 {
306         SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
307
308         return soup_message_headers_get_content_length (http->priv->msg->response_headers);
309 }
310
311 static const char *
312 soup_request_http_get_content_type (SoupRequest *request)
313 {
314         SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
315
316         return soup_message_headers_get_content_type (http->priv->msg->response_headers, NULL);
317 }
318
319 static const char *http_schemes[] = { "http", "https", NULL };
320
321 static void
322 soup_request_http_class_init (SoupRequestHTTPClass *request_http_class)
323 {
324         GObjectClass *object_class = G_OBJECT_CLASS (request_http_class);
325         SoupRequestClass *request_class =
326                 SOUP_REQUEST_CLASS (request_http_class);
327
328         g_type_class_add_private (request_http_class, sizeof (SoupRequestHTTPPrivate));
329
330         request_class->schemes = http_schemes;
331
332         object_class->finalize = soup_request_http_finalize;
333
334         request_class->check_uri = soup_request_http_check_uri;
335         request_class->send = soup_request_http_send;
336         request_class->send_async = soup_request_http_send_async;
337         request_class->send_finish = soup_request_http_send_finish;
338         request_class->get_content_length = soup_request_http_get_content_length;
339         request_class->get_content_type = soup_request_http_get_content_type;
340 }
341
342 /**
343  * soup_request_http_get_message:
344  * @http: a #SoupRequestHTTP object
345  *
346  * Gets a new reference to the #SoupMessage associated to this SoupRequest
347  *
348  * Returns: a new reference to the #SoupMessage
349  *
350  * Since: 2.34
351  */
352 SoupMessage *
353 soup_request_http_get_message (SoupRequestHTTP *http)
354 {
355         g_return_val_if_fail (SOUP_IS_REQUEST_HTTP (http), NULL);
356
357         return g_object_ref (http->priv->msg);
358 }