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, msg);
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 = soup_message_headers_get_content_type (msg->response_headers, NULL);
144 soup_message_content_sniffed (helper->original, content_type, NULL);
147 g_simple_async_result_complete (simple);
149 soup_message_finished (helper->original);
151 g_object_unref (simple);
153 /* Ask again for the resource, somehow the cache cannot locate it */
154 httpstream = soup_http_input_stream_new (session, helper->original);
155 soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
156 helper->cancellable, sent_async, simple);
159 /* It is in the cache but it was modified remotely */
160 httpstream = soup_http_input_stream_new (session, helper->original);
161 soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
162 helper->cancellable, sent_async, simple);
165 g_object_unref (helper->req);
166 g_object_unref (helper->original);
167 g_slice_free (ConditionalHelper, helper);
171 SoupRequestHTTP *http;
172 GAsyncReadyCallback callback;
174 SoupHTTPInputStream *httpstream;
177 static void soup_request_http_send_async (SoupRequest *request,
178 GCancellable *cancellable,
179 GAsyncReadyCallback callback,
183 send_async_cb (gpointer data)
185 GSimpleAsyncResult *simple;
186 SoupSession *session;
187 SendAsyncHelper *helper = (SendAsyncHelper *)data;
189 session = soup_request_get_session (SOUP_REQUEST (helper->http));
190 simple = g_simple_async_result_new (G_OBJECT (helper->http),
191 helper->callback, helper->user_data,
192 soup_request_http_send_async);
194 g_simple_async_result_set_op_res_gpointer (simple, helper->httpstream, g_object_unref);
196 /* Update message status */
197 soup_message_set_status (helper->http->priv->msg, SOUP_STATUS_OK);
200 soup_message_got_headers (helper->http->priv->msg);
202 if (soup_session_get_feature_for_message (session, SOUP_TYPE_CONTENT_SNIFFER, helper->http->priv->msg)) {
203 const char *content_type = soup_message_headers_get_content_type (helper->http->priv->msg->response_headers, NULL);
204 soup_message_content_sniffed (helper->http->priv->msg, content_type, NULL);
207 g_simple_async_result_complete (simple);
209 soup_message_finished (helper->http->priv->msg);
211 g_object_unref (simple);
213 g_object_unref (helper->http);
214 g_slice_free (SendAsyncHelper, helper);
220 soup_request_http_send_async (SoupRequest *request,
221 GCancellable *cancellable,
222 GAsyncReadyCallback callback,
225 SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
226 SoupHTTPInputStream *httpstream;
227 GSimpleAsyncResult *simple;
228 SoupSession *session;
231 session = soup_request_get_session (request);
232 cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
235 SoupCacheResponse response;
237 response = soup_cache_has_response (cache, http->priv->msg);
238 if (response == SOUP_CACHE_RESPONSE_FRESH) {
239 SoupHTTPInputStream *httpstream;
241 httpstream = (SoupHTTPInputStream *)
242 soup_cache_send_response (cache, http->priv->msg);
244 /* Cached resource file could have been deleted outside
247 /* Do return the stream asynchronously as in
248 * the other cases. It's not enough to use
249 * g_simple_async_result_complete_in_idle as
250 * the signals must be also emitted
253 SendAsyncHelper *helper = g_slice_new (SendAsyncHelper);
254 helper->http = g_object_ref (http);
255 helper->callback = callback;
256 helper->user_data = user_data;
257 helper->httpstream = httpstream;
258 g_timeout_add (0, send_async_cb, helper);
261 } else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
262 SoupMessage *conditional_msg;
263 ConditionalHelper *helper;
265 conditional_msg = soup_cache_generate_conditional_request (cache, http->priv->msg);
267 helper = g_slice_new0 (ConditionalHelper);
268 helper->req = g_object_ref (http);
269 helper->original = g_object_ref (http->priv->msg);
270 helper->cancellable = cancellable;
271 helper->callback = callback;
272 helper->user_data = user_data;
273 soup_session_queue_message (session, conditional_msg,
274 conditional_get_ready_cb,
280 simple = g_simple_async_result_new (G_OBJECT (http),
282 soup_request_http_send_async);
283 httpstream = soup_http_input_stream_new (soup_request_get_session (request),
285 soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
286 cancellable, sent_async, simple);
289 static GInputStream *
290 soup_request_http_send_finish (SoupRequest *request,
291 GAsyncResult *result,
294 GSimpleAsyncResult *simple;
296 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);
298 simple = G_SIMPLE_ASYNC_RESULT (result);
299 if (g_simple_async_result_propagate_error (simple, error))
301 return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
305 soup_request_http_get_content_length (SoupRequest *request)
307 SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
309 return soup_message_headers_get_content_length (http->priv->msg->response_headers);
313 soup_request_http_get_content_type (SoupRequest *request)
315 SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
317 return soup_message_headers_get_content_type (http->priv->msg->response_headers, NULL);
320 static const char *http_schemes[] = { "http", "https", NULL };
323 soup_request_http_class_init (SoupRequestHTTPClass *request_http_class)
325 GObjectClass *object_class = G_OBJECT_CLASS (request_http_class);
326 SoupRequestClass *request_class =
327 SOUP_REQUEST_CLASS (request_http_class);
329 g_type_class_add_private (request_http_class, sizeof (SoupRequestHTTPPrivate));
331 request_class->schemes = http_schemes;
333 object_class->finalize = soup_request_http_finalize;
335 request_class->check_uri = soup_request_http_check_uri;
336 request_class->send = soup_request_http_send;
337 request_class->send_async = soup_request_http_send_async;
338 request_class->send_finish = soup_request_http_send_finish;
339 request_class->get_content_length = soup_request_http_get_content_length;
340 request_class->get_content_type = soup_request_http_get_content_type;
344 * soup_request_http_get_message:
345 * @http: a #SoupRequestHTTP object
347 * Gets a new reference to the #SoupMessage associated to this SoupRequest
349 * Returns: a new reference to the #SoupMessage
352 soup_request_http_get_message (SoupRequestHTTP *http)
354 g_return_val_if_fail (SOUP_IS_REQUEST_HTTP (http), NULL);
356 return g_object_ref (http->priv->msg);