1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * soup-request.c: Protocol-independent streaming request interface
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-lib.h>
29
30 #include "soup-request.h"
31 #include "soup.h"
32 #include "soup-requester.h"
33
34 /**
35 * SECTION:soup-request
36 * @short_description: Protocol-independent streaming request interface
37 *
38 * A #SoupRequest is created by #SoupSession, and represents a request
39 * to retrieve a particular URI.
40 */
41
42 /**
43 * SoupRequest:
44 *
45 * A request to retrieve a particular URI.
46 *
47 * Since: 2.42
48 */
49
50 enum {
51 PROP_0,
52 PROP_URI,
53 PROP_SESSION
54 };
55
56 struct _SoupRequestPrivate {
57 SoupURI *uri;
58 SoupSession *session;
59 };
60
61 static void soup_request_initable_interface_init (GInitableIface *initable_interface);
62
G_DEFINE_TYPE_WITH_CODE(SoupRequest,soup_request,G_TYPE_OBJECT,G_ADD_PRIVATE (SoupRequest)G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,soup_request_initable_interface_init))63 G_DEFINE_TYPE_WITH_CODE (SoupRequest, soup_request, G_TYPE_OBJECT,
64 G_ADD_PRIVATE (SoupRequest)
65 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
66 soup_request_initable_interface_init))
67
68 static void
69 soup_request_init (SoupRequest *request)
70 {
71 request->priv = soup_request_get_instance_private (request);
72 }
73
74 static void
soup_request_finalize(GObject * object)75 soup_request_finalize (GObject *object)
76 {
77 SoupRequest *request = SOUP_REQUEST (object);
78
79 g_clear_pointer (&request->priv->uri, soup_uri_free);
80 g_clear_object (&request->priv->session);
81
82 G_OBJECT_CLASS (soup_request_parent_class)->finalize (object);
83 }
84
85 static void
soup_request_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)86 soup_request_set_property (GObject *object,
87 guint prop_id,
88 const GValue *value,
89 GParamSpec *pspec)
90 {
91 SoupRequest *request = SOUP_REQUEST (object);
92
93 switch (prop_id) {
94 case PROP_URI:
95 if (request->priv->uri)
96 soup_uri_free (request->priv->uri);
97 request->priv->uri = g_value_dup_boxed (value);
98 break;
99 case PROP_SESSION:
100 if (request->priv->session)
101 g_object_unref (request->priv->session);
102 request->priv->session = g_value_dup_object (value);
103 break;
104 default:
105 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
106 break;
107 }
108 }
109
110 static void
soup_request_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)111 soup_request_get_property (GObject *object,
112 guint prop_id,
113 GValue *value,
114 GParamSpec *pspec)
115 {
116 SoupRequest *request = SOUP_REQUEST (object);
117
118 switch (prop_id) {
119 case PROP_URI:
120 g_value_set_boxed (value, request->priv->uri);
121 break;
122 case PROP_SESSION:
123 g_value_set_object (value, request->priv->session);
124 break;
125 default:
126 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
127 break;
128 }
129 }
130
131 static gboolean
soup_request_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)132 soup_request_initable_init (GInitable *initable,
133 GCancellable *cancellable,
134 GError **error)
135 {
136 SoupRequest *request = SOUP_REQUEST (initable);
137 gboolean ok;
138
139 if (!request->priv->uri) {
140 g_set_error (error, SOUP_REQUEST_ERROR, SOUP_REQUEST_ERROR_BAD_URI,
141 _("No URI provided"));
142 return FALSE;
143 }
144
145 ok = SOUP_REQUEST_GET_CLASS (initable)->
146 check_uri (request, request->priv->uri, error);
147
148 if (!ok && error && !*error) {
149 char *uri_string = soup_uri_to_string (request->priv->uri, FALSE);
150 g_set_error (error, SOUP_REQUEST_ERROR, SOUP_REQUEST_ERROR_BAD_URI,
151 _("Invalid “%s” URI: %s"),
152 request->priv->uri->scheme,
153 uri_string);
154 g_free (uri_string);
155 }
156
157 return ok;
158 }
159
160 static gboolean
soup_request_default_check_uri(SoupRequest * request,SoupURI * uri,GError ** error)161 soup_request_default_check_uri (SoupRequest *request,
162 SoupURI *uri,
163 GError **error)
164 {
165 return TRUE;
166 }
167
168 /* Default implementation: assume the sync implementation doesn't block */
169 static void
soup_request_default_send_async(SoupRequest * request,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)170 soup_request_default_send_async (SoupRequest *request,
171 GCancellable *cancellable,
172 GAsyncReadyCallback callback,
173 gpointer user_data)
174 {
175 GTask *task;
176 GInputStream *stream;
177 GError *error = NULL;
178
179 task = g_task_new (request, cancellable, callback, user_data);
180
181 stream = soup_request_send (request, cancellable, &error);
182 if (stream)
183 g_task_return_pointer (task, stream, g_object_unref);
184 else
185 g_task_return_error (task, error);
186 g_object_unref (task);
187 }
188
189 static GInputStream *
soup_request_default_send_finish(SoupRequest * request,GAsyncResult * result,GError ** error)190 soup_request_default_send_finish (SoupRequest *request,
191 GAsyncResult *result,
192 GError **error)
193 {
194 return g_task_propagate_pointer (G_TASK (result), error);
195 }
196
197 /**
198 * soup_request_send:
199 * @request: a #SoupRequest
200 * @cancellable: a #GCancellable or %NULL
201 * @error: return location for a #GError, or %NULL
202 *
203 * Synchronously requests the URI pointed to by @request, and returns
204 * a #GInputStream that can be used to read its contents.
205 *
206 * Note that you cannot use this method with #SoupRequests attached to
207 * a #SoupSessionAsync.
208 *
209 * Return value: (transfer full): a #GInputStream that can be used to
210 * read from the URI pointed to by @request.
211 *
212 * Since: 2.42
213 */
214 GInputStream *
soup_request_send(SoupRequest * request,GCancellable * cancellable,GError ** error)215 soup_request_send (SoupRequest *request,
216 GCancellable *cancellable,
217 GError **error)
218 {
219 return SOUP_REQUEST_GET_CLASS (request)->
220 send (request, cancellable, error);
221 }
222
223 /**
224 * soup_request_send_async:
225 * @request: a #SoupRequest
226 * @cancellable: a #GCancellable or %NULL
227 * @callback: a #GAsyncReadyCallback
228 * @user_data: user data passed to @callback
229 *
230 * Begins an asynchronously request for the URI pointed to by
231 * @request.
232 *
233 * Note that you cannot use this method with #SoupRequests attached to
234 * a #SoupSessionSync.
235 *
236 * Since: 2.42
237 */
238 void
soup_request_send_async(SoupRequest * request,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)239 soup_request_send_async (SoupRequest *request,
240 GCancellable *cancellable,
241 GAsyncReadyCallback callback,
242 gpointer user_data)
243 {
244 SOUP_REQUEST_GET_CLASS (request)->
245 send_async (request, cancellable, callback, user_data);
246 }
247
248 /**
249 * soup_request_send_finish:
250 * @request: a #SoupRequest
251 * @result: the #GAsyncResult
252 * @error: return location for a #GError, or %NULL
253 *
254 * Gets the result of a soup_request_send_async().
255 *
256 * Return value: (transfer full): a #GInputStream that can be used to
257 * read from the URI pointed to by @request.
258 *
259 * Since: 2.42
260 */
261 GInputStream *
soup_request_send_finish(SoupRequest * request,GAsyncResult * result,GError ** error)262 soup_request_send_finish (SoupRequest *request,
263 GAsyncResult *result,
264 GError **error)
265 {
266 return SOUP_REQUEST_GET_CLASS (request)->
267 send_finish (request, result, error);
268 }
269
270 static void
soup_request_class_init(SoupRequestClass * request_class)271 soup_request_class_init (SoupRequestClass *request_class)
272 {
273 GObjectClass *object_class = G_OBJECT_CLASS (request_class);
274
275 request_class->check_uri = soup_request_default_check_uri;
276 request_class->send_async = soup_request_default_send_async;
277 request_class->send_finish = soup_request_default_send_finish;
278
279 object_class->finalize = soup_request_finalize;
280 object_class->set_property = soup_request_set_property;
281 object_class->get_property = soup_request_get_property;
282
283 /**
284 * SOUP_REQUEST_URI:
285 *
286 * Alias for the #SoupRequest:uri property, qv.
287 *
288 * Since: 2.42
289 */
290 /**
291 * SoupRequest:uri:
292 *
293 * The request URI.
294 *
295 * Since: 2.42
296 */
297 g_object_class_install_property (
298 object_class, PROP_URI,
299 g_param_spec_boxed (SOUP_REQUEST_URI,
300 "URI",
301 "The request URI",
302 SOUP_TYPE_URI,
303 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
304 G_PARAM_STATIC_STRINGS));
305 /**
306 * SOUP_REQUEST_SESSION:
307 *
308 * Alias for the #SoupRequest:session property, qv.
309 *
310 * Since: 2.42
311 */
312 /**
313 * SoupRequest:session:
314 *
315 * The request's #SoupSession.
316 *
317 * Since: 2.42
318 */
319 g_object_class_install_property (
320 object_class, PROP_SESSION,
321 g_param_spec_object (SOUP_REQUEST_SESSION,
322 "Session",
323 "The request's session",
324 SOUP_TYPE_SESSION,
325 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
326 G_PARAM_STATIC_STRINGS));
327 }
328
329 static void
soup_request_initable_interface_init(GInitableIface * initable_interface)330 soup_request_initable_interface_init (GInitableIface *initable_interface)
331 {
332 initable_interface->init = soup_request_initable_init;
333 }
334
335 /**
336 * soup_request_get_uri:
337 * @request: a #SoupRequest
338 *
339 * Gets @request's URI
340 *
341 * Return value: (transfer none): @request's URI
342 *
343 * Since: 2.42
344 */
345 SoupURI *
soup_request_get_uri(SoupRequest * request)346 soup_request_get_uri (SoupRequest *request)
347 {
348 return request->priv->uri;
349 }
350
351 /**
352 * soup_request_get_session:
353 * @request: a #SoupRequest
354 *
355 * Gets @request's #SoupSession
356 *
357 * Return value: (transfer none): @request's #SoupSession
358 *
359 * Since: 2.42
360 */
361 SoupSession *
soup_request_get_session(SoupRequest * request)362 soup_request_get_session (SoupRequest *request)
363 {
364 return request->priv->session;
365 }
366
367 /**
368 * soup_request_get_content_length:
369 * @request: a #SoupRequest
370 *
371 * Gets the length of the data represented by @request. For most
372 * request types, this will not be known until after you call
373 * soup_request_send() or soup_request_send_finish().
374 *
375 * Return value: the length of the data represented by @request,
376 * or -1 if not known.
377 *
378 * Since: 2.42
379 */
380 goffset
soup_request_get_content_length(SoupRequest * request)381 soup_request_get_content_length (SoupRequest *request)
382 {
383 return SOUP_REQUEST_GET_CLASS (request)->get_content_length (request);
384 }
385
386 /**
387 * soup_request_get_content_type:
388 * @request: a #SoupRequest
389 *
390 * Gets the type of the data represented by @request. For most request
391 * types, this will not be known until after you call
392 * soup_request_send() or soup_request_send_finish().
393 *
394 * As in the HTTP Content-Type header, this may include parameters
395 * after the MIME type.
396 *
397 * Return value: (nullable): the type of the data represented by
398 * @request, or %NULL if not known.
399 *
400 * Since: 2.42
401 */
402 const char *
soup_request_get_content_type(SoupRequest * request)403 soup_request_get_content_type (SoupRequest *request)
404 {
405 return SOUP_REQUEST_GET_CLASS (request)->get_content_type (request);
406 }
407