1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-request-http.c: http: URI request object
5 * Copyright (C) 2009 Red Hat, Inc.
6 * Copyright (C) 2010 Igalia, S.L.
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.
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.
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.
28 #include <glib/gi18n.h>
30 #include "soup-cache.h"
31 #include "soup-cache-private.h"
32 #include "soup-http-input-stream.h"
33 #include "soup-request-http.h"
35 G_DEFINE_TYPE (WebKitSoupRequestHTTP, webkit_soup_request_http, WEBKIT_TYPE_SOUP_REQUEST)
37 struct _WebKitSoupRequestHTTPPrivate {
42 * webkit_soup_request_http_get_message:
43 * @http: a #WebKitSoupRequestHTTP object
45 * Gets a new reference to the #SoupMessage associated to this SoupRequest
47 * Returns: a new reference to the #SoupMessage
50 webkit_soup_request_http_get_message (WebKitSoupRequestHTTP *http)
52 g_return_val_if_fail (WEBKIT_IS_SOUP_REQUEST_HTTP (http), NULL);
54 return g_object_ref (http->priv->msg);
58 webkit_soup_request_http_init (WebKitSoupRequestHTTP *http)
60 http->priv = G_TYPE_INSTANCE_GET_PRIVATE (http, WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPPrivate);
64 webkit_soup_request_http_check_uri (WebKitSoupRequest *request,
68 WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
70 if (!SOUP_URI_VALID_FOR_HTTP (uri))
73 http->priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
78 webkit_soup_request_http_finalize (GObject *object)
80 WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (object);
83 g_object_unref (http->priv->msg);
85 G_OBJECT_CLASS (webkit_soup_request_http_parent_class)->finalize (object);
89 webkit_soup_request_http_send (WebKitSoupRequest *request,
90 GCancellable *cancellable,
93 WebKitSoupHTTPInputStream *httpstream;
94 WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
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);
101 return (GInputStream *)httpstream;
106 sent_async (GObject *source, GAsyncResult *result, gpointer user_data)
108 WebKitSoupHTTPInputStream *httpstream = WEBKIT_SOUP_HTTP_INPUT_STREAM (source);
109 GSimpleAsyncResult *simple = user_data;
110 GError *error = NULL;
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);
115 g_simple_async_result_set_from_error (simple, error);
116 g_error_free (error);
117 g_object_unref (httpstream);
119 g_simple_async_result_complete (simple);
120 g_object_unref (simple);
125 WebKitSoupRequestHTTP *req;
126 SoupMessage *original;
127 GCancellable *cancellable;
128 GAsyncReadyCallback callback;
134 conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
136 ConditionalHelper *helper = (ConditionalHelper *)user_data;
137 GSimpleAsyncResult *simple;
138 WebKitSoupHTTPInputStream *httpstream;
140 simple = g_simple_async_result_new (G_OBJECT (helper->req),
141 helper->callback, helper->user_data,
142 conditional_get_ready_cb);
144 if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
145 WebKitSoupCache *cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
147 httpstream = (WebKitSoupHTTPInputStream *)webkit_soup_cache_send_response (cache, msg);
149 const gchar *content_type;
151 g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
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);
157 g_simple_async_result_complete (simple);
159 soup_message_finished (helper->original);
161 g_object_unref (simple);
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);
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);
175 g_object_unref (helper->req);
176 g_object_unref (helper->original);
177 g_slice_free (ConditionalHelper, helper);
181 WebKitSoupRequestHTTP *http;
182 GAsyncReadyCallback callback;
186 static void webkit_soup_request_http_send_async (WebKitSoupRequest *request,
187 GCancellable *cancellable,
188 GAsyncReadyCallback callback,
192 send_async_cb (gpointer data)
194 GSimpleAsyncResult *simple;
195 WebKitSoupHTTPInputStream *httpstream;
196 SoupSession *session;
197 WebKitSoupCache *cache;
198 SendAsyncHelper *helper = (SendAsyncHelper *)data;
200 session = webkit_soup_request_get_session (WEBKIT_SOUP_REQUEST (helper->http));
201 cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
203 httpstream = (WebKitSoupHTTPInputStream *)webkit_soup_cache_send_response (cache, SOUP_MESSAGE (helper->http->priv->msg));
206 const gchar *content_type;
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);
213 /* Update message status */
214 soup_message_set_status (helper->http->priv->msg, SOUP_STATUS_OK);
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);
221 g_simple_async_result_complete (simple);
223 soup_message_finished (helper->http->priv->msg);
225 g_object_unref (simple);
228 g_slice_free (SendAsyncHelper, helper);
234 webkit_soup_request_http_send_async (WebKitSoupRequest *request,
235 GCancellable *cancellable,
236 GAsyncReadyCallback callback,
239 WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
240 WebKitSoupHTTPInputStream *httpstream;
241 GSimpleAsyncResult *simple;
242 SoupSession *session;
243 WebKitSoupCache *cache;
245 session = webkit_soup_request_get_session (request);
246 cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE);
249 WebKitSoupCacheResponse response;
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
258 SendAsyncHelper *helper = g_slice_new (SendAsyncHelper);
260 helper->callback = callback;
261 helper->user_data = user_data;
262 g_timeout_add (0, send_async_cb, helper);
264 } else if (response == WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
265 SoupMessage *conditional_msg;
266 ConditionalHelper *helper;
268 conditional_msg = webkit_soup_cache_generate_conditional_request (cache, http->priv->msg);
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,
283 simple = g_simple_async_result_new (G_OBJECT (http),
285 webkit_soup_request_http_send_async);
286 httpstream = webkit_soup_http_input_stream_new (webkit_soup_request_get_session (request),
288 webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
289 cancellable, sent_async, simple);
292 static GInputStream *
293 webkit_soup_request_http_send_finish (WebKitSoupRequest *request,
294 GAsyncResult *result,
297 GSimpleAsyncResult *simple;
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);
301 simple = G_SIMPLE_ASYNC_RESULT (result);
302 if (g_simple_async_result_propagate_error (simple, error))
304 return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
308 webkit_soup_request_http_get_content_length (WebKitSoupRequest *request)
310 WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
312 return soup_message_headers_get_content_length (http->priv->msg->response_headers);
316 webkit_soup_request_http_get_content_type (WebKitSoupRequest *request)
318 WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request);
320 return soup_message_headers_get_content_type (http->priv->msg->response_headers, NULL);
324 webkit_soup_request_http_class_init (WebKitSoupRequestHTTPClass *request_http_class)
326 GObjectClass *object_class = G_OBJECT_CLASS (request_http_class);
327 WebKitSoupRequestClass *request_class =
328 WEBKIT_SOUP_REQUEST_CLASS (request_http_class);
330 g_type_class_add_private (request_http_class, sizeof (WebKitSoupRequestHTTPPrivate));
332 object_class->finalize = webkit_soup_request_http_finalize;
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;