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, 2010 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 #define LIBSOUP_USE_UNSTABLE_REQUEST_API
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"
41 G_DEFINE_TYPE (SoupRequestHTTP, soup_request_http, SOUP_TYPE_REQUEST)
43 struct _SoupRequestHTTPPrivate {
48 soup_request_http_init (SoupRequestHTTP *http)
50 http->priv = G_TYPE_INSTANCE_GET_PRIVATE (http, SOUP_TYPE_REQUEST_HTTP, SoupRequestHTTPPrivate);
54 soup_request_http_check_uri (SoupRequest *request,
58 SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
60 if (!SOUP_URI_VALID_FOR_HTTP (uri))
63 http->priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
68 soup_request_http_finalize (GObject *object)
70 SoupRequestHTTP *http = SOUP_REQUEST_HTTP (object);
73 g_object_unref (http->priv->msg);
75 G_OBJECT_CLASS (soup_request_http_parent_class)->finalize (object);
79 soup_request_http_send (SoupRequest *request,
80 GCancellable *cancellable,
83 SoupHTTPInputStream *httpstream;
84 SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
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);
91 return (GInputStream *)httpstream;
96 sent_async (GObject *source, GAsyncResult *result, gpointer user_data)
98 SoupHTTPInputStream *httpstream = SOUP_HTTP_INPUT_STREAM (source);
99 GSimpleAsyncResult *simple = user_data;
100 GError *error = NULL;
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);
105 g_simple_async_result_set_from_error (simple, error);
106 g_error_free (error);
107 g_object_unref (httpstream);
109 g_simple_async_result_complete (simple);
110 g_object_unref (simple);
115 SoupRequestHTTP *req;
116 SoupMessage *original;
117 GCancellable *cancellable;
118 GAsyncReadyCallback callback;
123 conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
125 ConditionalHelper *helper = (ConditionalHelper *)user_data;
126 GSimpleAsyncResult *simple;
127 SoupHTTPInputStream *httpstream;
129 simple = g_simple_async_result_new (G_OBJECT (helper->req),
130 helper->callback, helper->user_data,
131 conditional_get_ready_cb);
133 if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
134 SoupCache *cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
136 httpstream = (SoupHTTPInputStream *)soup_cache_send_response (cache, helper->original);
138 g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
140 soup_message_got_headers (helper->original);
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);
148 g_simple_async_result_complete (simple);
150 soup_message_finished (helper->original);
152 g_object_unref (simple);
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);
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);
166 g_object_unref (helper->req);
167 g_object_unref (helper->original);
168 g_slice_free (ConditionalHelper, helper);
172 SoupRequestHTTP *http;
173 GAsyncReadyCallback callback;
175 SoupHTTPInputStream *httpstream;
178 static void soup_request_http_send_async (SoupRequest *request,
179 GCancellable *cancellable,
180 GAsyncReadyCallback callback,
184 send_async_cb (gpointer data)
186 GSimpleAsyncResult *simple;
187 SoupSession *session;
188 SendAsyncHelper *helper = (SendAsyncHelper *)data;
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);
195 g_simple_async_result_set_op_res_gpointer (simple, helper->httpstream, g_object_unref);
198 soup_message_got_headers (helper->http->priv->msg);
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);
205 g_simple_async_result_complete (simple);
207 soup_message_finished (helper->http->priv->msg);
209 g_object_unref (simple);
211 g_object_unref (helper->http);
212 g_slice_free (SendAsyncHelper, helper);
218 soup_request_http_send_async (SoupRequest *request,
219 GCancellable *cancellable,
220 GAsyncReadyCallback callback,
223 SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
224 SoupHTTPInputStream *httpstream;
225 GSimpleAsyncResult *simple;
226 SoupSession *session;
229 session = soup_request_get_session (request);
230 cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
233 SoupCacheResponse response;
235 response = soup_cache_has_response (cache, http->priv->msg);
236 if (response == SOUP_CACHE_RESPONSE_FRESH) {
237 SoupHTTPInputStream *httpstream;
239 httpstream = (SoupHTTPInputStream *)
240 soup_cache_send_response (cache, http->priv->msg);
242 /* Cached resource file could have been deleted outside
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
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);
260 } else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
261 SoupMessage *conditional_msg;
262 ConditionalHelper *helper;
264 conditional_msg = soup_cache_generate_conditional_request (cache, http->priv->msg);
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,
279 simple = g_simple_async_result_new (G_OBJECT (http),
281 soup_request_http_send_async);
282 httpstream = soup_http_input_stream_new (soup_request_get_session (request),
284 soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
285 cancellable, sent_async, simple);
288 static GInputStream *
289 soup_request_http_send_finish (SoupRequest *request,
290 GAsyncResult *result,
293 GSimpleAsyncResult *simple;
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);
297 simple = G_SIMPLE_ASYNC_RESULT (result);
298 if (g_simple_async_result_propagate_error (simple, error))
300 return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
304 soup_request_http_get_content_length (SoupRequest *request)
306 SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
308 return soup_message_headers_get_content_length (http->priv->msg->response_headers);
312 soup_request_http_get_content_type (SoupRequest *request)
314 SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
316 return soup_message_headers_get_content_type (http->priv->msg->response_headers, NULL);
319 static const char *http_schemes[] = { "http", "https", NULL };
322 soup_request_http_class_init (SoupRequestHTTPClass *request_http_class)
324 GObjectClass *object_class = G_OBJECT_CLASS (request_http_class);
325 SoupRequestClass *request_class =
326 SOUP_REQUEST_CLASS (request_http_class);
328 g_type_class_add_private (request_http_class, sizeof (SoupRequestHTTPPrivate));
330 request_class->schemes = http_schemes;
332 object_class->finalize = soup_request_http_finalize;
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;
343 * soup_request_http_get_message:
344 * @http: a #SoupRequestHTTP object
346 * Gets a new reference to the #SoupMessage associated to this SoupRequest
348 * Returns: a new reference to the #SoupMessage
353 soup_request_http_get_message (SoupRequestHTTP *http)
355 g_return_val_if_fail (SOUP_IS_REQUEST_HTTP (http), NULL);
357 return g_object_ref (http->priv->msg);