/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 | G_OBJECT_CLASS (soup_body_input_stream_parent_class)->constructed (object); |
85 | |
|
86 | 0 | priv->base_stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (bistream)); |
87 | |
|
88 | 0 | if (priv->encoding == SOUP_ENCODING_NONE || |
89 | 0 | (priv->encoding == SOUP_ENCODING_CONTENT_LENGTH && |
90 | 0 | priv->read_length == 0)) |
91 | 0 | priv->eof = TRUE; |
92 | 0 | } |
93 | | |
94 | | static void |
95 | | soup_body_input_stream_set_property (GObject *object, guint prop_id, |
96 | | const GValue *value, GParamSpec *pspec) |
97 | 0 | { |
98 | 0 | SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object); |
99 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
100 | |
|
101 | 0 | switch (prop_id) { |
102 | 0 | case PROP_ENCODING: |
103 | 0 | priv->encoding = g_value_get_enum (value); |
104 | 0 | if (priv->encoding == SOUP_ENCODING_CHUNKED) |
105 | 0 | priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE; |
106 | 0 | break; |
107 | 0 | case PROP_CONTENT_LENGTH: |
108 | 0 | priv->read_length = g_value_get_int64 (value); |
109 | 0 | break; |
110 | 0 | default: |
111 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
112 | 0 | break; |
113 | 0 | } |
114 | 0 | } |
115 | | |
116 | | static void |
117 | | soup_body_input_stream_get_property (GObject *object, guint prop_id, |
118 | | GValue *value, GParamSpec *pspec) |
119 | 0 | { |
120 | 0 | SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object); |
121 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
122 | | |
123 | |
|
124 | 0 | switch (prop_id) { |
125 | 0 | case PROP_ENCODING: |
126 | 0 | g_value_set_enum (value, priv->encoding); |
127 | 0 | break; |
128 | 0 | case PROP_CONTENT_LENGTH: |
129 | 0 | g_assert_not_reached (); |
130 | 0 | break; |
131 | 0 | default: |
132 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
133 | 0 | break; |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | | static gssize |
138 | | soup_body_input_stream_read_raw (SoupBodyInputStream *bistream, |
139 | | void *buffer, |
140 | | gsize count, |
141 | | gboolean blocking, |
142 | | GCancellable *cancellable, |
143 | | GError **error) |
144 | 0 | { |
145 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
146 | 0 | gssize nread; |
147 | |
|
148 | 0 | if (!buffer && blocking) |
149 | 0 | nread = g_input_stream_skip (priv->base_stream, count, cancellable, error); |
150 | 0 | else |
151 | 0 | nread = g_pollable_stream_read (priv->base_stream, |
152 | 0 | buffer, count, |
153 | 0 | blocking, |
154 | 0 | cancellable, error); |
155 | 0 | if (nread == 0) { |
156 | 0 | priv->eof = TRUE; |
157 | 0 | if (priv->encoding != SOUP_ENCODING_EOF) { |
158 | 0 | g_set_error_literal (error, G_IO_ERROR, |
159 | 0 | G_IO_ERROR_PARTIAL_INPUT, |
160 | 0 | _("Connection terminated unexpectedly")); |
161 | 0 | return -1; |
162 | 0 | } |
163 | 0 | } |
164 | 0 | return nread; |
165 | 0 | } |
166 | | |
167 | | static gssize |
168 | | soup_body_input_stream_read_chunked (SoupBodyInputStream *bistream, |
169 | | void *buffer, |
170 | | gsize count, |
171 | | gboolean blocking, |
172 | | GCancellable *cancellable, |
173 | | GError **error) |
174 | 0 | { |
175 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
176 | 0 | SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (priv->base_stream); |
177 | 0 | char metabuf[128]; |
178 | 0 | gssize nread; |
179 | 0 | guint64 chunk_size; |
180 | 0 | gchar *end; |
181 | 0 | char *boundary; |
182 | 0 | gboolean got_line; |
183 | |
|
184 | 0 | again: |
185 | 0 | switch (priv->chunked_state) { |
186 | 0 | case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE: |
187 | 0 | nread = soup_filter_input_stream_read_until ( |
188 | 0 | fstream, metabuf, sizeof (metabuf), |
189 | 0 | "\r\n", 2, blocking, TRUE, |
190 | 0 | &got_line, cancellable, error); |
191 | 0 | if (nread < 0) |
192 | 0 | return nread; |
193 | | |
194 | 0 | if (nread == 0 || !got_line || nread < 3) { |
195 | 0 | if (error && *error == NULL) { |
196 | 0 | g_set_error_literal (error, G_IO_ERROR, |
197 | 0 | G_IO_ERROR_PARTIAL_INPUT, |
198 | 0 | _("Connection terminated unexpectedly")); |
199 | 0 | } |
200 | 0 | return -1; |
201 | 0 | } |
202 | | |
203 | | /* metabuf is not NUL-terminated by read_until(), so terminate |
204 | | * it at the trailing CRLF before scanning for chunk extensions. */ |
205 | 0 | metabuf[nread - 2] = '\0'; |
206 | | |
207 | | /* ignore extensions */ |
208 | 0 | boundary = memchr (metabuf, ';', nread); |
209 | 0 | if (boundary) |
210 | 0 | *boundary = '\0'; |
211 | |
|
212 | 0 | chunk_size = g_ascii_strtoull (metabuf, &end, 16); |
213 | 0 | if (*end) { |
214 | 0 | if (error && *error == NULL) { |
215 | 0 | g_set_error_literal (error, G_IO_ERROR, |
216 | 0 | G_IO_ERROR_INVALID_ARGUMENT, |
217 | 0 | _("Invalid chunk size")); |
218 | 0 | } |
219 | 0 | return -1; |
220 | 0 | } |
221 | | |
222 | 0 | if (chunk_size > G_MAXOFFSET) { |
223 | 0 | if (error && *error == NULL) { |
224 | 0 | g_set_error_literal (error, G_IO_ERROR, |
225 | 0 | G_IO_ERROR_MESSAGE_TOO_LARGE, |
226 | 0 | _("Too large chunk")); |
227 | 0 | } |
228 | 0 | return -1; |
229 | 0 | } |
230 | 0 | priv->read_length = (goffset)chunk_size; |
231 | 0 | if (priv->read_length > 0) |
232 | 0 | priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK; |
233 | 0 | else |
234 | 0 | priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_TRAILERS; |
235 | 0 | break; |
236 | | |
237 | 0 | case SOUP_BODY_INPUT_STREAM_STATE_CHUNK: |
238 | 0 | nread = soup_body_input_stream_read_raw ( |
239 | 0 | bistream, buffer, |
240 | 0 | MIN (count, priv->read_length), |
241 | 0 | blocking, cancellable, error); |
242 | 0 | if (nread > 0) { |
243 | 0 | priv->read_length -= nread; |
244 | 0 | if (priv->read_length == 0) |
245 | 0 | priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END; |
246 | 0 | } |
247 | 0 | return nread; |
248 | | |
249 | 0 | case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END: |
250 | 0 | nread = soup_filter_input_stream_read_until ( |
251 | 0 | SOUP_FILTER_INPUT_STREAM (priv->base_stream), |
252 | 0 | metabuf, sizeof (metabuf), |
253 | 0 | "\r\n", 2, blocking, TRUE, |
254 | 0 | &got_line, cancellable, error); |
255 | 0 | if (nread < 0) |
256 | 0 | return nread; |
257 | 0 | if (nread == 0 || !got_line) { |
258 | 0 | if (error && *error == NULL) { |
259 | 0 | g_set_error_literal (error, G_IO_ERROR, |
260 | 0 | G_IO_ERROR_PARTIAL_INPUT, |
261 | 0 | _("Connection terminated unexpectedly")); |
262 | 0 | } |
263 | 0 | return -1; |
264 | 0 | } |
265 | | |
266 | 0 | priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE; |
267 | 0 | break; |
268 | | |
269 | 0 | case SOUP_BODY_INPUT_STREAM_STATE_TRAILERS: |
270 | 0 | nread = soup_filter_input_stream_read_until ( |
271 | 0 | fstream, metabuf, sizeof (metabuf), |
272 | 0 | "\r\n", 2, blocking, TRUE, |
273 | 0 | &got_line, cancellable, error); |
274 | 0 | if (nread < 0) |
275 | 0 | return nread; |
276 | | |
277 | 0 | if (nread == 0) { |
278 | 0 | if (error && *error == NULL) { |
279 | 0 | g_set_error_literal (error, G_IO_ERROR, |
280 | 0 | G_IO_ERROR_PARTIAL_INPUT, |
281 | 0 | _("Connection terminated unexpectedly")); |
282 | 0 | } |
283 | 0 | return -1; |
284 | 0 | } |
285 | | |
286 | 0 | if (nread == 2 && strncmp (metabuf, "\r\n", nread) == 0) { |
287 | 0 | priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_DONE; |
288 | 0 | priv->eof = TRUE; |
289 | 0 | } |
290 | 0 | break; |
291 | | |
292 | 0 | case SOUP_BODY_INPUT_STREAM_STATE_DONE: |
293 | 0 | return 0; |
294 | 0 | } |
295 | | |
296 | 0 | goto again; |
297 | 0 | } |
298 | | |
299 | | static gssize |
300 | | read_internal (GInputStream *stream, |
301 | | void *buffer, |
302 | | gsize count, |
303 | | gboolean blocking, |
304 | | GCancellable *cancellable, |
305 | | GError **error) |
306 | 0 | { |
307 | 0 | SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream); |
308 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
309 | 0 | gssize nread; |
310 | |
|
311 | 0 | if (priv->eof) |
312 | 0 | return 0; |
313 | | |
314 | 0 | switch (priv->encoding) { |
315 | 0 | case SOUP_ENCODING_NONE: |
316 | 0 | return 0; |
317 | | |
318 | 0 | case SOUP_ENCODING_CHUNKED: |
319 | 0 | return soup_body_input_stream_read_chunked (bistream, buffer, count, |
320 | 0 | blocking, cancellable, error); |
321 | | |
322 | 0 | case SOUP_ENCODING_CONTENT_LENGTH: |
323 | 0 | case SOUP_ENCODING_EOF: |
324 | 0 | if (priv->read_length != -1) { |
325 | 0 | count = MIN (count, priv->read_length); |
326 | 0 | if (count == 0) |
327 | 0 | return 0; |
328 | 0 | } |
329 | | |
330 | 0 | nread = soup_body_input_stream_read_raw (bistream, buffer, count, |
331 | 0 | blocking, cancellable, error); |
332 | 0 | if (priv->read_length != -1 && nread > 0) { |
333 | 0 | priv->read_length -= nread; |
334 | |
|
335 | 0 | if (priv->encoding == SOUP_ENCODING_CONTENT_LENGTH && priv->read_length == 0) { |
336 | 0 | priv->eof = TRUE; |
337 | 0 | } |
338 | 0 | } |
339 | |
|
340 | 0 | if (priv->encoding == SOUP_ENCODING_CONTENT_LENGTH) |
341 | 0 | priv->pos += nread; |
342 | 0 | return nread; |
343 | | |
344 | 0 | default: |
345 | 0 | g_return_val_if_reached (-1); |
346 | 0 | } |
347 | 0 | } |
348 | | |
349 | | static gssize |
350 | | soup_body_input_stream_skip (GInputStream *stream, |
351 | | gsize count, |
352 | | GCancellable *cancellable, |
353 | | GError **error) |
354 | 0 | { |
355 | 0 | return read_internal (stream, NULL, count, TRUE, |
356 | 0 | cancellable, error); |
357 | 0 | } |
358 | | |
359 | | static gssize |
360 | | soup_body_input_stream_read_fn (GInputStream *stream, |
361 | | void *buffer, |
362 | | gsize count, |
363 | | GCancellable *cancellable, |
364 | | GError **error) |
365 | 0 | { |
366 | 0 | return read_internal (stream, buffer, count, TRUE, |
367 | 0 | cancellable, error); |
368 | 0 | } |
369 | | |
370 | | static gboolean |
371 | | soup_body_input_stream_close_fn (GInputStream *stream, |
372 | | GCancellable *cancellable, |
373 | | GError **error) |
374 | 0 | { |
375 | 0 | g_signal_emit (stream, signals[CLOSED], 0); |
376 | |
|
377 | 0 | return G_INPUT_STREAM_CLASS (soup_body_input_stream_parent_class)->close_fn (stream, cancellable, error); |
378 | 0 | } |
379 | | |
380 | | static gboolean |
381 | | soup_body_input_stream_is_readable (GPollableInputStream *stream) |
382 | 0 | { |
383 | 0 | SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream); |
384 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
385 | |
|
386 | 0 | return priv->eof || |
387 | 0 | g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (priv->base_stream)); |
388 | 0 | } |
389 | | |
390 | | static gboolean |
391 | | soup_body_input_stream_can_poll (GPollableInputStream *pollable) |
392 | 0 | { |
393 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (SOUP_BODY_INPUT_STREAM (pollable)); |
394 | 0 | GInputStream *base_stream = priv->base_stream; |
395 | |
|
396 | 0 | return G_IS_POLLABLE_INPUT_STREAM (base_stream) && |
397 | 0 | g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream)); |
398 | 0 | } |
399 | | |
400 | | static gssize |
401 | | soup_body_input_stream_read_nonblocking (GPollableInputStream *stream, |
402 | | void *buffer, |
403 | | gsize count, |
404 | | GError **error) |
405 | 0 | { |
406 | 0 | return read_internal (G_INPUT_STREAM (stream), buffer, count, FALSE, |
407 | 0 | NULL, error); |
408 | 0 | } |
409 | | |
410 | | static GSource * |
411 | | soup_body_input_stream_create_source (GPollableInputStream *stream, |
412 | | GCancellable *cancellable) |
413 | 0 | { |
414 | 0 | SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream); |
415 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (bistream); |
416 | 0 | GSource *base_source, *pollable_source; |
417 | |
|
418 | 0 | if (priv->eof) |
419 | 0 | base_source = g_timeout_source_new (0); |
420 | 0 | else |
421 | 0 | base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (priv->base_stream), cancellable); |
422 | 0 | g_source_set_dummy_callback (base_source); |
423 | |
|
424 | 0 | pollable_source = g_pollable_source_new (G_OBJECT (stream)); |
425 | 0 | g_source_add_child_source (pollable_source, base_source); |
426 | 0 | g_source_unref (base_source); |
427 | |
|
428 | 0 | return pollable_source; |
429 | 0 | } |
430 | | |
431 | | static void |
432 | | soup_body_input_stream_class_init (SoupBodyInputStreamClass *stream_class) |
433 | 0 | { |
434 | 0 | GObjectClass *object_class = G_OBJECT_CLASS (stream_class); |
435 | 0 | GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (stream_class); |
436 | |
|
437 | 0 | object_class->constructed = soup_body_input_stream_constructed; |
438 | 0 | object_class->set_property = soup_body_input_stream_set_property; |
439 | 0 | object_class->get_property = soup_body_input_stream_get_property; |
440 | |
|
441 | 0 | input_stream_class->skip = soup_body_input_stream_skip; |
442 | 0 | input_stream_class->read_fn = soup_body_input_stream_read_fn; |
443 | 0 | input_stream_class->close_fn = soup_body_input_stream_close_fn; |
444 | |
|
445 | 0 | signals[CLOSED] = |
446 | 0 | g_signal_new ("closed", |
447 | 0 | G_OBJECT_CLASS_TYPE (object_class), |
448 | 0 | G_SIGNAL_RUN_LAST, |
449 | 0 | 0, |
450 | 0 | NULL, NULL, |
451 | 0 | NULL, |
452 | 0 | G_TYPE_NONE, 0); |
453 | |
|
454 | 0 | properties[PROP_ENCODING] = |
455 | 0 | g_param_spec_enum ("encoding", |
456 | 0 | "Encoding", |
457 | 0 | "Message body encoding", |
458 | 0 | SOUP_TYPE_ENCODING, |
459 | 0 | SOUP_ENCODING_NONE, |
460 | 0 | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); |
461 | |
|
462 | 0 | properties[PROP_CONTENT_LENGTH] = |
463 | 0 | g_param_spec_int64 ("content-length", |
464 | 0 | "Content-Length", |
465 | 0 | "Message body Content-Length", |
466 | 0 | -1, G_MAXINT64, -1, |
467 | 0 | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); |
468 | |
|
469 | 0 | g_object_class_install_properties (object_class, LAST_PROPERTY, properties); |
470 | 0 | } |
471 | | |
472 | | static void |
473 | | soup_body_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, |
474 | | gpointer interface_data) |
475 | 0 | { |
476 | 0 | pollable_interface->can_poll = soup_body_input_stream_can_poll; |
477 | 0 | pollable_interface->is_readable = soup_body_input_stream_is_readable; |
478 | 0 | pollable_interface->read_nonblocking = soup_body_input_stream_read_nonblocking; |
479 | 0 | pollable_interface->create_source = soup_body_input_stream_create_source; |
480 | 0 | } |
481 | | |
482 | | static goffset |
483 | | soup_body_input_stream_tell (GSeekable *seekable) |
484 | 0 | { |
485 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (SOUP_BODY_INPUT_STREAM (seekable)); |
486 | 0 | return priv->pos; |
487 | 0 | } |
488 | | |
489 | | static gboolean |
490 | | soup_body_input_stream_can_seek (GSeekable *seekable) |
491 | 0 | { |
492 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (SOUP_BODY_INPUT_STREAM (seekable)); |
493 | |
|
494 | 0 | return priv->encoding == SOUP_ENCODING_CONTENT_LENGTH |
495 | 0 | && G_IS_SEEKABLE (priv->base_stream) |
496 | 0 | && g_seekable_can_seek (G_SEEKABLE (priv->base_stream)); |
497 | 0 | } |
498 | | |
499 | | static gboolean |
500 | | soup_body_input_stream_seek (GSeekable *seekable, |
501 | | goffset offset, |
502 | | GSeekType type, |
503 | | GCancellable *cancellable, |
504 | | GError **error) |
505 | 0 | { |
506 | 0 | SoupBodyInputStreamPrivate *priv = soup_body_input_stream_get_instance_private (SOUP_BODY_INPUT_STREAM (seekable)); |
507 | 0 | goffset position, end_position; |
508 | |
|
509 | 0 | end_position = priv->pos + priv->read_length; |
510 | 0 | switch (type) { |
511 | 0 | case G_SEEK_CUR: |
512 | 0 | position = priv->pos + offset; |
513 | 0 | break; |
514 | 0 | case G_SEEK_SET: |
515 | 0 | position = offset; |
516 | 0 | break; |
517 | 0 | case G_SEEK_END: |
518 | 0 | position = end_position + offset; |
519 | 0 | break; |
520 | 0 | default: |
521 | 0 | g_return_val_if_reached (FALSE); |
522 | 0 | } |
523 | | |
524 | 0 | if (position < 0 || position >= end_position) { |
525 | 0 | g_set_error_literal (error, |
526 | 0 | G_IO_ERROR, |
527 | 0 | G_IO_ERROR_INVALID_ARGUMENT, |
528 | 0 | _("Invalid seek request")); |
529 | 0 | return FALSE; |
530 | 0 | } |
531 | | |
532 | 0 | if (!g_seekable_seek (G_SEEKABLE (priv->base_stream), position - priv->pos, |
533 | 0 | G_SEEK_CUR, cancellable, error)) |
534 | 0 | return FALSE; |
535 | | |
536 | 0 | priv->pos = position; |
537 | |
|
538 | 0 | return TRUE; |
539 | 0 | } |
540 | | |
541 | | static gboolean |
542 | | soup_body_input_stream_can_truncate (GSeekable *seekable) |
543 | 0 | { |
544 | 0 | return FALSE; |
545 | 0 | } |
546 | | |
547 | | static gboolean |
548 | | soup_body_input_stream_truncate_fn (GSeekable *seekable, |
549 | | goffset offset, |
550 | | GCancellable *cancellable, |
551 | | GError **error) |
552 | 0 | { |
553 | 0 | g_set_error_literal (error, |
554 | 0 | G_IO_ERROR, |
555 | 0 | G_IO_ERROR_NOT_SUPPORTED, |
556 | 0 | _("Cannot truncate SoupBodyInputStream")); |
557 | 0 | return FALSE; |
558 | 0 | } |
559 | | |
560 | | static void |
561 | | soup_body_input_stream_seekable_init (GSeekableIface *seekable_interface) |
562 | 0 | { |
563 | 0 | seekable_interface->tell = soup_body_input_stream_tell; |
564 | 0 | seekable_interface->can_seek = soup_body_input_stream_can_seek; |
565 | 0 | seekable_interface->seek = soup_body_input_stream_seek; |
566 | 0 | seekable_interface->can_truncate = soup_body_input_stream_can_truncate; |
567 | 0 | seekable_interface->truncate_fn = soup_body_input_stream_truncate_fn; |
568 | 0 | } |
569 | | |
570 | | GInputStream * |
571 | | soup_body_input_stream_new (GInputStream *base_stream, |
572 | | SoupEncoding encoding, |
573 | | goffset content_length) |
574 | 0 | { |
575 | 0 | if (encoding == SOUP_ENCODING_CHUNKED) |
576 | 0 | g_return_val_if_fail (SOUP_IS_FILTER_INPUT_STREAM (base_stream), NULL); |
577 | | |
578 | 0 | return g_object_new (SOUP_TYPE_BODY_INPUT_STREAM, |
579 | 0 | "base-stream", base_stream, |
580 | 0 | "close-base-stream", FALSE, |
581 | 0 | "encoding", encoding, |
582 | 0 | "content-length", content_length, |
583 | | NULL); |
584 | 0 | } |