/src/libsoup/libsoup/soup-client-input-stream.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ |
2 | | /* |
3 | | * soup-client-input-stream.c |
4 | | * |
5 | | * Copyright 2010-2012 Red Hat, Inc. |
6 | | */ |
7 | | |
8 | | #ifdef HAVE_CONFIG_H |
9 | | #include <config.h> |
10 | | #endif |
11 | | |
12 | | #include "soup-client-input-stream.h" |
13 | | #include "soup.h" |
14 | | #include "soup-message-private.h" |
15 | | #include "soup-message-metrics-private.h" |
16 | | #include "soup-misc.h" |
17 | | |
18 | | struct _SoupClientInputStream { |
19 | | SoupFilterInputStream parent_instance; |
20 | | }; |
21 | | |
22 | | typedef struct { |
23 | | SoupMessage *msg; |
24 | | SoupMessageMetrics *metrics; |
25 | | } SoupClientInputStreamPrivate; |
26 | | |
27 | | enum { |
28 | | SIGNAL_EOF, |
29 | | LAST_SIGNAL |
30 | | }; |
31 | | |
32 | | static guint signals[LAST_SIGNAL] = { 0 }; |
33 | | |
34 | | enum { |
35 | | PROP_0, |
36 | | |
37 | | PROP_MESSAGE, |
38 | | |
39 | | LAST_PROPERTY |
40 | | }; |
41 | | |
42 | | static GParamSpec *properties[LAST_PROPERTY] = { NULL, }; |
43 | | |
44 | | static GPollableInputStreamInterface *soup_client_input_stream_parent_pollable_interface; |
45 | | static void soup_client_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data); |
46 | | |
47 | | G_DEFINE_FINAL_TYPE_WITH_CODE (SoupClientInputStream, soup_client_input_stream, SOUP_TYPE_FILTER_INPUT_STREAM, |
48 | | G_ADD_PRIVATE (SoupClientInputStream) |
49 | | G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM, |
50 | | soup_client_input_stream_pollable_init)) |
51 | | |
52 | | static void |
53 | | soup_client_input_stream_init (SoupClientInputStream *stream) |
54 | 0 | { |
55 | 0 | } |
56 | | |
57 | | static void |
58 | | soup_client_input_stream_finalize (GObject *object) |
59 | 0 | { |
60 | 0 | SoupClientInputStream *cistream = SOUP_CLIENT_INPUT_STREAM (object); |
61 | 0 | SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (cistream); |
62 | |
|
63 | 0 | g_clear_object (&priv->msg); |
64 | |
|
65 | 0 | G_OBJECT_CLASS (soup_client_input_stream_parent_class)->finalize (object); |
66 | 0 | } |
67 | | |
68 | | static void |
69 | | soup_client_input_stream_set_property (GObject *object, guint prop_id, |
70 | | const GValue *value, GParamSpec *pspec) |
71 | 0 | { |
72 | 0 | SoupClientInputStream *cistream = SOUP_CLIENT_INPUT_STREAM (object); |
73 | 0 | SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (cistream); |
74 | |
|
75 | 0 | switch (prop_id) { |
76 | 0 | case PROP_MESSAGE: |
77 | 0 | priv->msg = g_value_dup_object (value); |
78 | 0 | priv->metrics = soup_message_get_metrics (priv->msg); |
79 | 0 | break; |
80 | 0 | default: |
81 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
82 | 0 | break; |
83 | 0 | } |
84 | 0 | } |
85 | | |
86 | | static void |
87 | | soup_client_input_stream_get_property (GObject *object, guint prop_id, |
88 | | GValue *value, GParamSpec *pspec) |
89 | 0 | { |
90 | 0 | SoupClientInputStream *cistream = SOUP_CLIENT_INPUT_STREAM (object); |
91 | 0 | SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (cistream); |
92 | |
|
93 | 0 | switch (prop_id) { |
94 | 0 | case PROP_MESSAGE: |
95 | 0 | g_value_set_object (value, priv->msg); |
96 | 0 | break; |
97 | 0 | default: |
98 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
99 | 0 | break; |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | | static gssize |
104 | | soup_client_input_stream_read_fn (GInputStream *stream, |
105 | | void *buffer, |
106 | | gsize count, |
107 | | GCancellable *cancellable, |
108 | | GError **error) |
109 | 0 | { |
110 | 0 | SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (SOUP_CLIENT_INPUT_STREAM (stream)); |
111 | 0 | gssize nread; |
112 | |
|
113 | 0 | if (g_cancellable_set_error_if_cancelled (soup_message_io_get_cancellable (priv->msg), error)) |
114 | 0 | return -1; |
115 | | |
116 | 0 | nread = G_INPUT_STREAM_CLASS (soup_client_input_stream_parent_class)-> |
117 | 0 | read_fn (stream, buffer, count, cancellable, error); |
118 | |
|
119 | 0 | if (priv->metrics && nread > 0) |
120 | 0 | priv->metrics->response_body_size += nread; |
121 | |
|
122 | 0 | if (nread == 0) |
123 | 0 | g_signal_emit (stream, signals[SIGNAL_EOF], 0); |
124 | |
|
125 | 0 | return nread; |
126 | 0 | } |
127 | | |
128 | | static gssize |
129 | | soup_client_input_stream_skip (GInputStream *stream, |
130 | | gsize count, |
131 | | GCancellable *cancellable, |
132 | | GError **error) |
133 | 0 | { |
134 | 0 | SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (SOUP_CLIENT_INPUT_STREAM (stream)); |
135 | 0 | gssize nread; |
136 | |
|
137 | 0 | if (g_cancellable_set_error_if_cancelled (soup_message_io_get_cancellable (priv->msg), error)) |
138 | 0 | return -1; |
139 | | |
140 | 0 | nread = G_INPUT_STREAM_CLASS (soup_client_input_stream_parent_class)-> |
141 | 0 | skip (stream, count, cancellable, error); |
142 | |
|
143 | 0 | if (priv->metrics && nread > 0) |
144 | 0 | priv->metrics->response_body_size += nread; |
145 | |
|
146 | 0 | if (nread == 0) |
147 | 0 | g_signal_emit (stream, signals[SIGNAL_EOF], 0); |
148 | |
|
149 | 0 | return nread; |
150 | 0 | } |
151 | | |
152 | | static gssize |
153 | | soup_client_input_stream_read_nonblocking (GPollableInputStream *stream, |
154 | | void *buffer, |
155 | | gsize count, |
156 | | GError **error) |
157 | 0 | { |
158 | 0 | SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (SOUP_CLIENT_INPUT_STREAM (stream)); |
159 | 0 | gssize nread; |
160 | |
|
161 | 0 | if (g_cancellable_set_error_if_cancelled (soup_message_io_get_cancellable (priv->msg), error)) |
162 | 0 | return -1; |
163 | | |
164 | 0 | nread = soup_client_input_stream_parent_pollable_interface-> |
165 | 0 | read_nonblocking (stream, buffer, count, error); |
166 | |
|
167 | 0 | if (priv->metrics && nread > 0) |
168 | 0 | priv->metrics->response_body_size += nread; |
169 | |
|
170 | 0 | if (nread == 0) |
171 | 0 | g_signal_emit (stream, signals[SIGNAL_EOF], 0); |
172 | |
|
173 | 0 | return nread; |
174 | 0 | } |
175 | | |
176 | | static gboolean |
177 | | soup_client_input_stream_close_fn (GInputStream *stream, |
178 | | GCancellable *cancellable, |
179 | | GError **error) |
180 | 0 | { |
181 | 0 | SoupClientInputStream *cistream = SOUP_CLIENT_INPUT_STREAM (stream); |
182 | 0 | SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (cistream); |
183 | 0 | gboolean success; |
184 | |
|
185 | 0 | success = soup_message_io_skip (priv->msg, TRUE, cancellable, error); |
186 | 0 | soup_message_io_finished (priv->msg); |
187 | 0 | return success; |
188 | 0 | } |
189 | | |
190 | | static gboolean |
191 | | close_async_ready (SoupMessage *msg, gpointer user_data) |
192 | 0 | { |
193 | 0 | GTask *task = user_data; |
194 | 0 | SoupClientInputStream *cistream = g_task_get_source_object (task); |
195 | 0 | SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (cistream); |
196 | 0 | GError *error = NULL; |
197 | |
|
198 | 0 | if (!soup_message_io_skip (priv->msg, FALSE, g_task_get_cancellable (task), &error) && |
199 | 0 | g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { |
200 | 0 | g_error_free (error); |
201 | 0 | return TRUE; |
202 | 0 | } |
203 | | |
204 | 0 | soup_message_io_finished (priv->msg); |
205 | |
|
206 | 0 | if (error) |
207 | 0 | g_task_return_error (task, error); |
208 | 0 | else |
209 | 0 | g_task_return_boolean (task, TRUE); |
210 | 0 | g_object_unref (task); |
211 | |
|
212 | 0 | return FALSE; |
213 | 0 | } |
214 | | |
215 | | static void |
216 | | soup_client_input_stream_close_async (GInputStream *stream, |
217 | | gint priority, |
218 | | GCancellable *cancellable, |
219 | | GAsyncReadyCallback callback, |
220 | | gpointer user_data) |
221 | 0 | { |
222 | 0 | SoupClientInputStream *cistream = SOUP_CLIENT_INPUT_STREAM (stream); |
223 | 0 | SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (cistream); |
224 | 0 | GTask *task; |
225 | 0 | GSource *source; |
226 | |
|
227 | 0 | task = g_task_new (stream, cancellable, callback, user_data); |
228 | 0 | g_task_set_source_tag (task, soup_client_input_stream_close_async); |
229 | 0 | g_task_set_priority (task, priority); |
230 | |
|
231 | 0 | if (close_async_ready (priv->msg, task) == G_SOURCE_CONTINUE) { |
232 | | /* When SoupClientInputStream is created we always have a body input stream, |
233 | | * and we finished writing, so it's safe to pass NULL for the streams |
234 | | */ |
235 | 0 | source = soup_message_io_data_get_source ((SoupMessageIOData *)soup_message_get_io_data (priv->msg), |
236 | 0 | G_OBJECT (priv->msg), |
237 | 0 | NULL, NULL, |
238 | 0 | cancellable, NULL, NULL); |
239 | |
|
240 | 0 | g_task_attach_source (task, source, (GSourceFunc) close_async_ready); |
241 | 0 | g_source_unref (source); |
242 | 0 | } |
243 | 0 | } |
244 | | |
245 | | static gboolean |
246 | | soup_client_input_stream_close_finish (GInputStream *stream, |
247 | | GAsyncResult *result, |
248 | | GError **error) |
249 | 0 | { |
250 | 0 | return g_task_propagate_boolean (G_TASK (result), error); |
251 | 0 | } |
252 | | |
253 | | static void |
254 | | soup_client_input_stream_class_init (SoupClientInputStreamClass *stream_class) |
255 | 0 | { |
256 | 0 | GObjectClass *object_class = G_OBJECT_CLASS (stream_class); |
257 | 0 | GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (stream_class); |
258 | |
|
259 | 0 | object_class->finalize = soup_client_input_stream_finalize; |
260 | 0 | object_class->set_property = soup_client_input_stream_set_property; |
261 | 0 | object_class->get_property = soup_client_input_stream_get_property; |
262 | |
|
263 | 0 | input_stream_class->read_fn = soup_client_input_stream_read_fn; |
264 | 0 | input_stream_class->skip = soup_client_input_stream_skip; |
265 | 0 | input_stream_class->close_fn = soup_client_input_stream_close_fn; |
266 | 0 | input_stream_class->close_async = soup_client_input_stream_close_async; |
267 | 0 | input_stream_class->close_finish = soup_client_input_stream_close_finish; |
268 | |
|
269 | 0 | signals[SIGNAL_EOF] = |
270 | 0 | g_signal_new ("eof", |
271 | 0 | G_OBJECT_CLASS_TYPE (object_class), |
272 | 0 | G_SIGNAL_RUN_LAST, |
273 | 0 | 0, |
274 | 0 | NULL, NULL, |
275 | 0 | NULL, |
276 | 0 | G_TYPE_NONE, 0); |
277 | |
|
278 | 0 | properties[PROP_MESSAGE] = |
279 | 0 | g_param_spec_object ("message", |
280 | 0 | "Message", |
281 | 0 | "Message", |
282 | 0 | SOUP_TYPE_MESSAGE, |
283 | 0 | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | |
284 | 0 | G_PARAM_STATIC_STRINGS); |
285 | |
|
286 | 0 | g_object_class_install_properties (object_class, LAST_PROPERTY, properties); |
287 | 0 | } |
288 | | |
289 | | static void |
290 | | soup_client_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, |
291 | | gpointer interface_data) |
292 | 0 | { |
293 | 0 | soup_client_input_stream_parent_pollable_interface = |
294 | 0 | g_type_interface_peek_parent (pollable_interface); |
295 | |
|
296 | 0 | pollable_interface->read_nonblocking = soup_client_input_stream_read_nonblocking; |
297 | 0 | } |
298 | | |
299 | | GInputStream * |
300 | | soup_client_input_stream_new (GInputStream *base_stream, |
301 | | SoupMessage *msg) |
302 | 0 | { |
303 | 0 | return g_object_new (SOUP_TYPE_CLIENT_INPUT_STREAM, |
304 | 0 | "base-stream", base_stream, |
305 | 0 | "message", msg, |
306 | 0 | NULL); |
307 | 0 | } |