/src/libsoup/libsoup/http1/soup-body-input-stream.c
Line | Count | Source |
1 | | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ |
2 | | /* |
3 | | * soup-body-input-stream.c |
4 | | * |
5 | | * Copyright 2012 Red Hat, Inc. |
6 | | */ |
7 | | |
8 | | #ifdef HAVE_CONFIG_H |
9 | | #include <config.h> |
10 | | #endif |
11 | | |
12 | | #include <stdlib.h> |
13 | | |
14 | | #include <glib/gi18n-lib.h> |
15 | | |
16 | | #include "soup-body-input-stream.h" |
17 | | #include "soup.h" |
18 | | #include "soup-filter-input-stream.h" |
19 | | |
20 | | typedef enum { |
21 | | SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE, |
22 | | SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END, |
23 | | SOUP_BODY_INPUT_STREAM_STATE_CHUNK, |
24 | | SOUP_BODY_INPUT_STREAM_STATE_TRAILERS, |
25 | | SOUP_BODY_INPUT_STREAM_STATE_DONE |
26 | | } SoupBodyInputStreamState; |
27 | | |
28 | | struct _SoupBodyInputStream { |
29 | | GFilterInputStream parent_instance; |
30 | | }; |
31 | | |
32 | | typedef struct { |
33 | | GInputStream *base_stream; |
34 | | |
35 | | SoupEncoding encoding; |
36 | | goffset read_length; |
37 | | SoupBodyInputStreamState chunked_state; |
38 | | gboolean eof; |
39 | | |
40 | | goffset pos; |
41 | | } SoupBodyInputStreamPrivate; |
42 | | |
43 | | enum { |
44 | | CLOSED, |
45 | | LAST_SIGNAL |
46 | | }; |
47 | | |
48 | | static guint signals[LAST_SIGNAL] = { 0 }; |
49 | | |
50 | | enum { |
51 | | PROP_0, |
52 | | |
53 | | PROP_ENCODING, |
54 | | PROP_CONTENT_LENGTH, |
55 | | |
56 | | LAST_PROPERTY |
57 | | }; |
58 | | |
59 | | static GParamSpec *properties[LAST_PROPERTY] = { NULL, }; |
60 | | |
61 | | static void soup_body_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data); |
62 | | static void soup_body_input_stream_seekable_init (GSeekableIface *seekable_interface); |
63 | | |
64 | 0 | G_DEFINE_FINAL_TYPE_WITH_CODE (SoupBodyInputStream, soup_body_input_stream, G_TYPE_FILTER_INPUT_STREAM, |
65 | 0 | G_ADD_PRIVATE (SoupBodyInputStream) |
66 | 0 | G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM, |
67 | 0 | soup_body_input_stream_pollable_init) |
68 | 0 | G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, |
69 | 0 | soup_body_input_stream_seekable_init)) |
70 | 0 |
|
71 | 0 | static void |
72 | 0 | soup_body_input_stream_init (SoupBodyInputStream *bistream) |
73 | 0 | { |
74 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
75 | 0 | priv->encoding = SOUP_ENCODING_NONE; |
76 | 0 | } |
77 | | |
78 | | static void |
79 | | soup_body_input_stream_constructed (GObject *object) |
80 | 0 | { |
81 | 0 | SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object); |
82 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
83 | |
|
84 | 0 | priv->base_stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (bistream)); |
85 | |
|
86 | 0 | if (priv->encoding == SOUP_ENCODING_NONE || |
87 | 0 | (priv->encoding == SOUP_ENCODING_CONTENT_LENGTH && |
88 | 0 | priv->read_length == 0)) |
89 | 0 | priv->eof = TRUE; |
90 | 0 | } |
91 | | |
92 | | static void |
93 | | soup_body_input_stream_set_property (GObject *object, guint prop_id, |
94 | | const GValue *value, GParamSpec *pspec) |
95 | 0 | { |
96 | 0 | SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object); |
97 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
98 | |
|
99 | 0 | switch (prop_id) { |
100 | 0 | case PROP_ENCODING: |
101 | 0 | priv->encoding = g_value_get_enum (value); |
102 | 0 | if (priv->encoding == SOUP_ENCODING_CHUNKED) |
103 | 0 | priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE; |
104 | 0 | break; |
105 | 0 | case PROP_CONTENT_LENGTH: |
106 | 0 | priv->read_length = g_value_get_int64 (value); |
107 | 0 | break; |
108 | 0 | default: |
109 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
110 | 0 | break; |
111 | 0 | } |
112 | 0 | } |
113 | | |
114 | | static void |
115 | | soup_body_input_stream_get_property (GObject *object, guint prop_id, |
116 | | GValue *value, GParamSpec *pspec) |
117 | 0 | { |
118 | 0 | SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object); |
119 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
120 | | |
121 | |
|
122 | 0 | switch (prop_id) { |
123 | 0 | case PROP_ENCODING: |
124 | 0 | g_value_set_enum (value, priv->encoding); |
125 | 0 | break; |
126 | 0 | default: |
127 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
128 | 0 | break; |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | | static gssize |
133 | | soup_body_input_stream_read_raw (SoupBodyInputStream *bistream, |
134 | | void *buffer, |
135 | | gsize count, |
136 | | gboolean blocking, |
137 | | GCancellable *cancellable, |
138 | | GError **error) |
139 | 0 | { |
140 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
141 | 0 | gssize nread; |
142 | |
|
143 | 0 | if (!buffer && blocking) |
144 | 0 | nread = g_input_stream_skip (priv->base_stream, count, cancellable, error); |
145 | 0 | else |
146 | 0 | nread = g_pollable_stream_read (priv->base_stream, |
147 | 0 | buffer, count, |
148 | 0 | blocking, |
149 | 0 | cancellable, error); |
150 | 0 | if (nread == 0) { |
151 | 0 | priv->eof = TRUE; |
152 | 0 | if (priv->encoding != SOUP_ENCODING_EOF) { |
153 | 0 | g_set_error_literal (error, G_IO_ERROR, |
154 | 0 | G_IO_ERROR_PARTIAL_INPUT, |
155 | 0 | _("Connection terminated unexpectedly")); |
156 | 0 | return -1; |
157 | 0 | } |
158 | 0 | } |
159 | 0 | return nread; |
160 | 0 | } |
161 | | |
162 | | static gssize |
163 | | soup_body_input_stream_read_chunked (SoupBodyInputStream *bistream, |
164 | | void *buffer, |
165 | | gsize count, |
166 | | gboolean blocking, |
167 | | GCancellable *cancellable, |
168 | | GError **error) |
169 | 0 | { |
170 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
171 | 0 | SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (priv->base_stream); |
172 | 0 | char metabuf[128]; |
173 | 0 | gssize nread; |
174 | 0 | gboolean got_line; |
175 | |
|
176 | 0 | again: |
177 | 0 | switch (priv->chunked_state) { |
178 | 0 | case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE: |
179 | 0 | nread = soup_filter_input_stream_read_line ( |
180 | 0 | fstream, metabuf, sizeof (metabuf), blocking, |
181 | 0 | &got_line, cancellable, error); |
182 | 0 | if (nread < 0) |
183 | 0 | return nread; |
184 | 0 | if (nread == 0 || !got_line) { |
185 | 0 | if (error && *error == NULL) { |
186 | 0 | g_set_error_literal (error, G_IO_ERROR, |
187 | 0 | G_IO_ERROR_PARTIAL_INPUT, |
188 | 0 | _("Connection terminated unexpectedly")); |
189 | 0 | } |
190 | 0 | return -1; |
191 | 0 | } |
192 | | |
193 | 0 | priv->read_length = strtoul (metabuf, NULL, 16); |
194 | 0 | if (priv->read_length > 0) |
195 | 0 | priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK; |
196 | 0 | else |
197 | 0 | priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_TRAILERS; |
198 | 0 | break; |
199 | | |
200 | 0 | case SOUP_BODY_INPUT_STREAM_STATE_CHUNK: |
201 | 0 | nread = soup_body_input_stream_read_raw ( |
202 | 0 | bistream, buffer, |
203 | 0 | MIN (count, priv->read_length), |
204 | 0 | blocking, cancellable, error); |
205 | 0 | if (nread > 0) { |
206 | 0 | priv->read_length -= nread; |
207 | 0 | if (priv->read_length == 0) |
208 | 0 | priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END; |
209 | 0 | } |
210 | 0 | return nread; |
211 | | |
212 | 0 | case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END: |
213 | 0 | nread = soup_filter_input_stream_read_line ( |
214 | 0 | SOUP_FILTER_INPUT_STREAM (priv->base_stream), |
215 | 0 | metabuf, sizeof (metabuf), blocking, |
216 | 0 | &got_line, cancellable, error); |
217 | 0 | if (nread < 0) |
218 | 0 | return nread; |
219 | 0 | if (nread == 0 || !got_line) { |
220 | 0 | if (error && *error == NULL) { |
221 | 0 | g_set_error_literal (error, G_IO_ERROR, |
222 | 0 | G_IO_ERROR_PARTIAL_INPUT, |
223 | 0 | _("Connection terminated unexpectedly")); |
224 | 0 | } |
225 | 0 | return -1; |
226 | 0 | } |
227 | | |
228 | 0 | priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE; |
229 | 0 | break; |
230 | | |
231 | 0 | case SOUP_BODY_INPUT_STREAM_STATE_TRAILERS: |
232 | 0 | nread = soup_filter_input_stream_read_line ( |
233 | 0 | fstream, metabuf, sizeof (metabuf), blocking, |
234 | 0 | &got_line, cancellable, error); |
235 | 0 | if (nread < 0) |
236 | 0 | return nread; |
237 | | |
238 | 0 | if (nread == 0) { |
239 | 0 | if (error && *error == NULL) { |
240 | 0 | g_set_error_literal (error, G_IO_ERROR, |
241 | 0 | G_IO_ERROR_PARTIAL_INPUT, |
242 | 0 | _("Connection terminated unexpectedly")); |
243 | 0 | } |
244 | 0 | return -1; |
245 | 0 | } |
246 | | |
247 | 0 | if ((nread == 2 && strncmp (metabuf, "\r\n", nread) == 0) || (nread == 1 && strncmp (metabuf, "\n", nread) == 0)) { |
248 | 0 | priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_DONE; |
249 | 0 | priv->eof = TRUE; |
250 | 0 | } |
251 | 0 | break; |
252 | | |
253 | 0 | case SOUP_BODY_INPUT_STREAM_STATE_DONE: |
254 | 0 | return 0; |
255 | 0 | } |
256 | | |
257 | 0 | goto again; |
258 | 0 | } |
259 | | |
260 | | static gssize |
261 | | read_internal (GInputStream *stream, |
262 | | void *buffer, |
263 | | gsize count, |
264 | | gboolean blocking, |
265 | | GCancellable *cancellable, |
266 | | GError **error) |
267 | 0 | { |
268 | 0 | SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream); |
269 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
270 | 0 | gssize nread; |
271 | |
|
272 | 0 | if (priv->eof) |
273 | 0 | return 0; |
274 | | |
275 | 0 | switch (priv->encoding) { |
276 | 0 | case SOUP_ENCODING_NONE: |
277 | 0 | return 0; |
278 | | |
279 | 0 | case SOUP_ENCODING_CHUNKED: |
280 | 0 | return soup_body_input_stream_read_chunked (bistream, buffer, count, |
281 | 0 | blocking, cancellable, error); |
282 | | |
283 | 0 | case SOUP_ENCODING_CONTENT_LENGTH: |
284 | 0 | case SOUP_ENCODING_EOF: |
285 | 0 | if (priv->read_length != -1) { |
286 | 0 | count = MIN (count, priv->read_length); |
287 | 0 | if (count == 0) |
288 | 0 | return 0; |
289 | 0 | } |
290 | | |
291 | 0 | nread = soup_body_input_stream_read_raw (bistream, buffer, count, |
292 | 0 | blocking, cancellable, error); |
293 | 0 | if (priv->read_length != -1 && nread > 0) { |
294 | 0 | priv->read_length -= nread; |
295 | |
|
296 | 0 | if (priv->encoding == SOUP_ENCODING_CONTENT_LENGTH && priv->read_length == 0) { |
297 | 0 | priv->eof = TRUE; |
298 | 0 | } |
299 | 0 | } |
300 | |
|
301 | 0 | if (priv->encoding == SOUP_ENCODING_CONTENT_LENGTH) |
302 | 0 | priv->pos += nread; |
303 | 0 | return nread; |
304 | | |
305 | 0 | default: |
306 | 0 | g_return_val_if_reached (-1); |
307 | 0 | } |
308 | 0 | } |
309 | | |
310 | | static gssize |
311 | | soup_body_input_stream_skip (GInputStream *stream, |
312 | | gsize count, |
313 | | GCancellable *cancellable, |
314 | | GError **error) |
315 | 0 | { |
316 | 0 | return read_internal (stream, NULL, count, TRUE, |
317 | 0 | cancellable, error); |
318 | 0 | } |
319 | | |
320 | | static gssize |
321 | | soup_body_input_stream_read_fn (GInputStream *stream, |
322 | | void *buffer, |
323 | | gsize count, |
324 | | GCancellable *cancellable, |
325 | | GError **error) |
326 | 0 | { |
327 | 0 | return read_internal (stream, buffer, count, TRUE, |
328 | 0 | cancellable, error); |
329 | 0 | } |
330 | | |
331 | | static gboolean |
332 | | soup_body_input_stream_close_fn (GInputStream *stream, |
333 | | GCancellable *cancellable, |
334 | | GError **error) |
335 | 0 | { |
336 | 0 | g_signal_emit (stream, signals[CLOSED], 0); |
337 | |
|
338 | 0 | return G_INPUT_STREAM_CLASS (soup_body_input_stream_parent_class)->close_fn (stream, cancellable, error); |
339 | 0 | } |
340 | | |
341 | | static gboolean |
342 | | soup_body_input_stream_is_readable (GPollableInputStream *stream) |
343 | 0 | { |
344 | 0 | SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream); |
345 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
346 | |
|
347 | 0 | return priv->eof || |
348 | 0 | g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (priv->base_stream)); |
349 | 0 | } |
350 | | |
351 | | static gboolean |
352 | | soup_body_input_stream_can_poll (GPollableInputStream *pollable) |
353 | 0 | { |
354 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (SOUP_BODY_INPUT_STREAM (pollable)); |
355 | 0 | GInputStream *base_stream = priv->base_stream; |
356 | |
|
357 | 0 | return G_IS_POLLABLE_INPUT_STREAM (base_stream) && |
358 | 0 | g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream)); |
359 | 0 | } |
360 | | |
361 | | static gssize |
362 | | soup_body_input_stream_read_nonblocking (GPollableInputStream *stream, |
363 | | void *buffer, |
364 | | gsize count, |
365 | | GError **error) |
366 | 0 | { |
367 | 0 | return read_internal (G_INPUT_STREAM (stream), buffer, count, FALSE, |
368 | 0 | NULL, error); |
369 | 0 | } |
370 | | |
371 | | static GSource * |
372 | | soup_body_input_stream_create_source (GPollableInputStream *stream, |
373 | | GCancellable *cancellable) |
374 | 0 | { |
375 | 0 | SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream); |
376 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
377 | 0 | GSource *base_source, *pollable_source; |
378 | |
|
379 | 0 | if (priv->eof) |
380 | 0 | base_source = g_timeout_source_new (0); |
381 | 0 | else |
382 | 0 | base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (priv->base_stream), cancellable); |
383 | 0 | g_source_set_dummy_callback (base_source); |
384 | |
|
385 | 0 | pollable_source = g_pollable_source_new (G_OBJECT (stream)); |
386 | 0 | g_source_add_child_source (pollable_source, base_source); |
387 | 0 | g_source_unref (base_source); |
388 | |
|
389 | 0 | return pollable_source; |
390 | 0 | } |
391 | | |
392 | | static void |
393 | | soup_body_input_stream_class_init (SoupBodyInputStreamClass *stream_class) |
394 | 0 | { |
395 | 0 | GObjectClass *object_class = G_OBJECT_CLASS (stream_class); |
396 | 0 | GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (stream_class); |
397 | |
|
398 | 0 | object_class->constructed = soup_body_input_stream_constructed; |
399 | 0 | object_class->set_property = soup_body_input_stream_set_property; |
400 | 0 | object_class->get_property = soup_body_input_stream_get_property; |
401 | |
|
402 | 0 | input_stream_class->skip = soup_body_input_stream_skip; |
403 | 0 | input_stream_class->read_fn = soup_body_input_stream_read_fn; |
404 | 0 | input_stream_class->close_fn = soup_body_input_stream_close_fn; |
405 | |
|
406 | 0 | signals[CLOSED] = |
407 | 0 | g_signal_new ("closed", |
408 | 0 | G_OBJECT_CLASS_TYPE (object_class), |
409 | 0 | G_SIGNAL_RUN_LAST, |
410 | 0 | 0, |
411 | 0 | NULL, NULL, |
412 | 0 | NULL, |
413 | 0 | G_TYPE_NONE, 0); |
414 | |
|
415 | 0 | properties[PROP_ENCODING] = |
416 | 0 | g_param_spec_enum ("encoding", |
417 | 0 | "Encoding", |
418 | 0 | "Message body encoding", |
419 | 0 | SOUP_TYPE_ENCODING, |
420 | 0 | SOUP_ENCODING_NONE, |
421 | 0 | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); |
422 | |
|
423 | 0 | properties[PROP_CONTENT_LENGTH] = |
424 | 0 | g_param_spec_int64 ("content-length", |
425 | 0 | "Content-Length", |
426 | 0 | "Message body Content-Length", |
427 | 0 | -1, G_MAXINT64, -1, |
428 | 0 | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); |
429 | |
|
430 | 0 | g_object_class_install_properties (object_class, LAST_PROPERTY, properties); |
431 | 0 | } |
432 | | |
433 | | static void |
434 | | soup_body_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, |
435 | | gpointer interface_data) |
436 | 0 | { |
437 | 0 | pollable_interface->can_poll = soup_body_input_stream_can_poll; |
438 | 0 | pollable_interface->is_readable = soup_body_input_stream_is_readable; |
439 | 0 | pollable_interface->read_nonblocking = soup_body_input_stream_read_nonblocking; |
440 | 0 | pollable_interface->create_source = soup_body_input_stream_create_source; |
441 | 0 | } |
442 | | |
443 | | static goffset |
444 | | soup_body_input_stream_tell (GSeekable *seekable) |
445 | 0 | { |
446 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (SOUP_BODY_INPUT_STREAM (seekable)); |
447 | 0 | return priv->pos; |
448 | 0 | } |
449 | | |
450 | | static gboolean |
451 | | soup_body_input_stream_can_seek (GSeekable *seekable) |
452 | 0 | { |
453 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (SOUP_BODY_INPUT_STREAM (seekable)); |
454 | |
|
455 | 0 | return priv->encoding == SOUP_ENCODING_CONTENT_LENGTH |
456 | 0 | && G_IS_SEEKABLE (priv->base_stream) |
457 | 0 | && g_seekable_can_seek (G_SEEKABLE (priv->base_stream)); |
458 | 0 | } |
459 | | |
460 | | static gboolean |
461 | | soup_body_input_stream_seek (GSeekable *seekable, |
462 | | goffset offset, |
463 | | GSeekType type, |
464 | | GCancellable *cancellable, |
465 | | GError **error) |
466 | 0 | { |
467 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (SOUP_BODY_INPUT_STREAM (seekable)); |
468 | 0 | goffset position, end_position; |
469 | |
|
470 | 0 | end_position = priv->pos + priv->read_length; |
471 | 0 | switch (type) { |
472 | 0 | case G_SEEK_CUR: |
473 | 0 | position = priv->pos + offset; |
474 | 0 | break; |
475 | 0 | case G_SEEK_SET: |
476 | 0 | position = offset; |
477 | 0 | break; |
478 | 0 | case G_SEEK_END: |
479 | 0 | position = end_position + offset; |
480 | 0 | break; |
481 | 0 | default: |
482 | 0 | g_return_val_if_reached (FALSE); |
483 | 0 | } |
484 | | |
485 | 0 | if (position < 0 || position >= end_position) { |
486 | 0 | g_set_error_literal (error, |
487 | 0 | G_IO_ERROR, |
488 | 0 | G_IO_ERROR_INVALID_ARGUMENT, |
489 | 0 | _("Invalid seek request")); |
490 | 0 | return FALSE; |
491 | 0 | } |
492 | | |
493 | 0 | if (!g_seekable_seek (G_SEEKABLE (priv->base_stream), position - priv->pos, |
494 | 0 | G_SEEK_CUR, cancellable, error)) |
495 | 0 | return FALSE; |
496 | | |
497 | 0 | priv->pos = position; |
498 | |
|
499 | 0 | return TRUE; |
500 | 0 | } |
501 | | |
502 | | static gboolean |
503 | | soup_body_input_stream_can_truncate (GSeekable *seekable) |
504 | 0 | { |
505 | 0 | return FALSE; |
506 | 0 | } |
507 | | |
508 | | static gboolean |
509 | | soup_body_input_stream_truncate_fn (GSeekable *seekable, |
510 | | goffset offset, |
511 | | GCancellable *cancellable, |
512 | | GError **error) |
513 | 0 | { |
514 | 0 | g_set_error_literal (error, |
515 | 0 | G_IO_ERROR, |
516 | 0 | G_IO_ERROR_NOT_SUPPORTED, |
517 | 0 | _("Cannot truncate SoupBodyInputStream")); |
518 | 0 | return FALSE; |
519 | 0 | } |
520 | | |
521 | | static void |
522 | | soup_body_input_stream_seekable_init (GSeekableIface *seekable_interface) |
523 | 0 | { |
524 | 0 | seekable_interface->tell = soup_body_input_stream_tell; |
525 | 0 | seekable_interface->can_seek = soup_body_input_stream_can_seek; |
526 | 0 | seekable_interface->seek = soup_body_input_stream_seek; |
527 | 0 | seekable_interface->can_truncate = soup_body_input_stream_can_truncate; |
528 | 0 | seekable_interface->truncate_fn = soup_body_input_stream_truncate_fn; |
529 | 0 | } |
530 | | |
531 | | GInputStream * |
532 | | soup_body_input_stream_new (GInputStream *base_stream, |
533 | | SoupEncoding encoding, |
534 | | goffset content_length) |
535 | 0 | { |
536 | 0 | if (encoding == SOUP_ENCODING_CHUNKED) |
537 | 0 | g_return_val_if_fail (SOUP_IS_FILTER_INPUT_STREAM (base_stream), NULL); |
538 | | |
539 | 0 | return g_object_new (SOUP_TYPE_BODY_INPUT_STREAM, |
540 | 0 | "base-stream", base_stream, |
541 | 0 | "close-base-stream", FALSE, |
542 | 0 | "encoding", encoding, |
543 | 0 | "content-length", content_length, |
544 | | NULL); |
545 | 0 | } |