1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-body-input-stream.c
5 * Copyright 2012 Red Hat, Inc.
14 #include <glib/gi18n-lib.h>
16 #include "soup-body-input-stream.h"
18 #include "soup-filter-input-stream.h"
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;
28 struct _SoupBodyInputStreamPrivate {
29 GInputStream *base_stream;
31 SoupEncoding encoding;
33 SoupBodyInputStreamState chunked_state;
44 static guint signals[LAST_SIGNAL] = { 0 };
53 static void soup_body_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
54 static void soup_body_input_stream_seekable_init (GSeekableIface *seekable_interface);
56 G_DEFINE_TYPE_WITH_CODE (SoupBodyInputStream, soup_body_input_stream, G_TYPE_FILTER_INPUT_STREAM,
57 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
58 soup_body_input_stream_pollable_init)
59 G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
60 soup_body_input_stream_seekable_init))
63 soup_body_input_stream_init (SoupBodyInputStream *bistream)
65 bistream->priv = G_TYPE_INSTANCE_GET_PRIVATE (bistream,
66 SOUP_TYPE_BODY_INPUT_STREAM,
67 SoupBodyInputStreamPrivate);
68 bistream->priv->encoding = SOUP_ENCODING_NONE;
72 soup_body_input_stream_constructed (GObject *object)
74 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object);
76 bistream->priv->base_stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (bistream));
78 if (bistream->priv->encoding == SOUP_ENCODING_NONE ||
79 (bistream->priv->encoding == SOUP_ENCODING_CONTENT_LENGTH &&
80 bistream->priv->read_length == 0))
81 bistream->priv->eof = TRUE;
85 soup_body_input_stream_set_property (GObject *object, guint prop_id,
86 const GValue *value, GParamSpec *pspec)
88 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object);
92 bistream->priv->encoding = g_value_get_enum (value);
93 if (bistream->priv->encoding == SOUP_ENCODING_CHUNKED)
94 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE;
96 case PROP_CONTENT_LENGTH:
97 bistream->priv->read_length = g_value_get_int64 (value);
100 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
106 soup_body_input_stream_get_property (GObject *object, guint prop_id,
107 GValue *value, GParamSpec *pspec)
109 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object);
113 g_value_set_enum (value, bistream->priv->encoding);
116 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
122 soup_body_input_stream_read_raw (SoupBodyInputStream *bistream,
126 GCancellable *cancellable,
131 nread = g_pollable_stream_read (bistream->priv->base_stream,
136 bistream->priv->eof = TRUE;
137 if (bistream->priv->encoding != SOUP_ENCODING_EOF) {
138 g_set_error_literal (error, G_IO_ERROR,
139 G_IO_ERROR_PARTIAL_INPUT,
140 _("Connection terminated unexpectedly"));
148 soup_body_input_stream_read_chunked (SoupBodyInputStream *bistream,
152 GCancellable *cancellable,
155 SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (bistream->priv->base_stream);
161 switch (bistream->priv->chunked_state) {
162 case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE:
163 nread = soup_filter_input_stream_read_line (
164 fstream, metabuf, sizeof (metabuf), blocking,
165 &got_line, cancellable, error);
169 g_set_error_literal (error, G_IO_ERROR,
170 G_IO_ERROR_PARTIAL_INPUT,
171 _("Connection terminated unexpectedly"));
175 bistream->priv->read_length = strtoul (metabuf, NULL, 16);
176 if (bistream->priv->read_length > 0)
177 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK;
179 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_TRAILERS;
182 case SOUP_BODY_INPUT_STREAM_STATE_CHUNK:
183 nread = soup_body_input_stream_read_raw (
185 MIN (count, bistream->priv->read_length),
186 blocking, cancellable, error);
188 bistream->priv->read_length -= nread;
189 if (bistream->priv->read_length == 0)
190 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END;
194 case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END:
195 nread = soup_filter_input_stream_read_line (
196 SOUP_FILTER_INPUT_STREAM (bistream->priv->base_stream),
197 metabuf, sizeof (metabuf), blocking,
198 &got_line, cancellable, error);
202 g_set_error_literal (error, G_IO_ERROR,
203 G_IO_ERROR_PARTIAL_INPUT,
204 _("Connection terminated unexpectedly"));
208 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE;
211 case SOUP_BODY_INPUT_STREAM_STATE_TRAILERS:
212 nread = soup_filter_input_stream_read_line (
213 fstream, buffer, count, blocking,
214 &got_line, cancellable, error);
218 if (strncmp (buffer, "\r\n", nread) || strncmp (buffer, "\n", nread)) {
219 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_DONE;
220 bistream->priv->eof = TRUE;
224 case SOUP_BODY_INPUT_STREAM_STATE_DONE:
232 read_internal (GInputStream *stream,
236 GCancellable *cancellable,
239 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream);
242 if (bistream->priv->eof)
245 switch (bistream->priv->encoding) {
246 case SOUP_ENCODING_NONE:
249 case SOUP_ENCODING_CHUNKED:
250 return soup_body_input_stream_read_chunked (bistream, buffer, count,
251 blocking, cancellable, error);
253 case SOUP_ENCODING_CONTENT_LENGTH:
254 case SOUP_ENCODING_EOF:
255 if (bistream->priv->read_length != -1) {
256 count = MIN (count, bistream->priv->read_length);
261 nread = soup_body_input_stream_read_raw (bistream, buffer, count,
262 blocking, cancellable, error);
263 if (bistream->priv->read_length != -1 && nread > 0)
264 bistream->priv->read_length -= nread;
266 if (bistream->priv->encoding == SOUP_ENCODING_CONTENT_LENGTH)
267 bistream->priv->pos += nread;
271 g_return_val_if_reached (-1);
276 soup_body_input_stream_skip (GInputStream *stream,
278 GCancellable *cancellable,
281 SoupBodyInputStreamPrivate *priv = SOUP_BODY_INPUT_STREAM(stream)->priv;
284 skipped = g_input_stream_skip (G_FILTER_INPUT_STREAM (stream)->base_stream,
285 MIN (count, priv->read_length),
290 else if (skipped > 0)
291 priv->pos += skipped;
297 soup_body_input_stream_read_fn (GInputStream *stream,
300 GCancellable *cancellable,
303 return read_internal (stream, buffer, count, TRUE,
308 soup_body_input_stream_close_fn (GInputStream *stream,
309 GCancellable *cancellable,
312 g_signal_emit (stream, signals[CLOSED], 0);
314 return G_INPUT_STREAM_CLASS (soup_body_input_stream_parent_class)->close_fn (stream, cancellable, error);
318 soup_body_input_stream_is_readable (GPollableInputStream *stream)
320 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream);
322 return bistream->priv->eof ||
323 g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (bistream->priv->base_stream));
327 soup_body_input_stream_can_poll (GPollableInputStream *pollable)
329 GInputStream *base_stream = SOUP_BODY_INPUT_STREAM (pollable)->priv->base_stream;
331 return G_IS_POLLABLE_INPUT_STREAM (base_stream) &&
332 g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream));
336 soup_body_input_stream_read_nonblocking (GPollableInputStream *stream,
341 return read_internal (G_INPUT_STREAM (stream), buffer, count, FALSE,
346 soup_body_input_stream_create_source (GPollableInputStream *stream,
347 GCancellable *cancellable)
349 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream);
350 GSource *base_source, *pollable_source;
352 if (bistream->priv->eof)
353 base_source = g_timeout_source_new (0);
355 base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (bistream->priv->base_stream), cancellable);
356 g_source_set_dummy_callback (base_source);
358 pollable_source = g_pollable_source_new (G_OBJECT (stream));
359 g_source_add_child_source (pollable_source, base_source);
360 g_source_unref (base_source);
362 return pollable_source;
366 soup_body_input_stream_class_init (SoupBodyInputStreamClass *stream_class)
368 GObjectClass *object_class = G_OBJECT_CLASS (stream_class);
369 GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (stream_class);
371 g_type_class_add_private (stream_class, sizeof (SoupBodyInputStreamPrivate));
373 object_class->constructed = soup_body_input_stream_constructed;
374 object_class->set_property = soup_body_input_stream_set_property;
375 object_class->get_property = soup_body_input_stream_get_property;
377 input_stream_class->skip = soup_body_input_stream_skip;
378 input_stream_class->read_fn = soup_body_input_stream_read_fn;
379 input_stream_class->close_fn = soup_body_input_stream_close_fn;
382 g_signal_new ("closed",
383 G_OBJECT_CLASS_TYPE (object_class),
390 g_object_class_install_property (
391 object_class, PROP_ENCODING,
392 g_param_spec_enum ("encoding",
394 "Message body encoding",
397 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
398 g_object_class_install_property (
399 object_class, PROP_CONTENT_LENGTH,
400 g_param_spec_int64 ("content-length",
402 "Message body Content-Length",
404 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
408 soup_body_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
409 gpointer interface_data)
411 pollable_interface->can_poll = soup_body_input_stream_can_poll;
412 pollable_interface->is_readable = soup_body_input_stream_is_readable;
413 pollable_interface->read_nonblocking = soup_body_input_stream_read_nonblocking;
414 pollable_interface->create_source = soup_body_input_stream_create_source;
418 soup_body_input_stream_tell (GSeekable *seekable)
420 return SOUP_BODY_INPUT_STREAM (seekable)->priv->pos;
424 soup_body_input_stream_can_seek (GSeekable *seekable)
426 SoupBodyInputStreamPrivate *priv = SOUP_BODY_INPUT_STREAM (seekable)->priv;
428 return priv->encoding == SOUP_ENCODING_CONTENT_LENGTH
429 && G_IS_SEEKABLE (priv->base_stream)
430 && g_seekable_can_seek (G_SEEKABLE (priv->base_stream));
434 soup_body_input_stream_seek (GSeekable *seekable,
437 GCancellable *cancellable,
440 SoupBodyInputStreamPrivate *priv = SOUP_BODY_INPUT_STREAM (seekable)->priv;
441 goffset position, end_position;
443 end_position = priv->pos + priv->read_length;
446 position = priv->pos + offset;
452 position = end_position + offset;
455 g_return_val_if_reached (FALSE);
458 if (position < 0 || position >= end_position) {
459 g_set_error_literal (error,
461 G_IO_ERROR_INVALID_ARGUMENT,
462 _("Invalid seek request"));
466 if (!g_seekable_seek (G_SEEKABLE (priv->base_stream), position - priv->pos,
467 G_SEEK_CUR, cancellable, error))
470 priv->pos = position;
476 soup_body_input_stream_can_truncate (GSeekable *seekable)
482 soup_body_input_stream_truncate_fn (GSeekable *seekable,
484 GCancellable *cancellable,
487 g_set_error_literal (error,
489 G_IO_ERROR_NOT_SUPPORTED,
490 _("Cannot truncate SoupBodyInputStream"));
495 soup_body_input_stream_seekable_init (GSeekableIface *seekable_interface)
497 seekable_interface->tell = soup_body_input_stream_tell;
498 seekable_interface->can_seek = soup_body_input_stream_can_seek;
499 seekable_interface->seek = soup_body_input_stream_seek;
500 seekable_interface->can_truncate = soup_body_input_stream_can_truncate;
501 seekable_interface->truncate_fn = soup_body_input_stream_truncate_fn;
505 soup_body_input_stream_new (GInputStream *base_stream,
506 SoupEncoding encoding,
507 goffset content_length)
509 if (encoding == SOUP_ENCODING_CHUNKED)
510 g_return_val_if_fail (SOUP_IS_FILTER_INPUT_STREAM (base_stream), NULL);
512 return g_object_new (SOUP_TYPE_BODY_INPUT_STREAM,
513 "base-stream", base_stream,
514 "close-base-stream", FALSE,
515 "encoding", encoding,
516 "content-length", content_length,