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.
16 #include <glib/gi18n-lib.h>
18 #include "soup-body-input-stream.h"
19 #include "soup-enum-types.h"
20 #include "soup-filter-input-stream.h"
21 #include "soup-marshal.h"
22 #include "soup-message-headers.h"
25 SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE,
26 SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END,
27 SOUP_BODY_INPUT_STREAM_STATE_CHUNK,
28 SOUP_BODY_INPUT_STREAM_STATE_TRAILERS,
29 SOUP_BODY_INPUT_STREAM_STATE_DONE
30 } SoupBodyInputStreamState;
32 struct _SoupBodyInputStreamPrivate {
33 GInputStream *base_stream;
35 SoupEncoding encoding;
37 SoupBodyInputStreamState chunked_state;
46 static guint signals[LAST_SIGNAL] = { 0 };
55 static void soup_body_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
57 G_DEFINE_TYPE_WITH_CODE (SoupBodyInputStream, soup_body_input_stream, G_TYPE_FILTER_INPUT_STREAM,
58 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
59 soup_body_input_stream_pollable_init))
62 soup_body_input_stream_init (SoupBodyInputStream *bistream)
64 bistream->priv = G_TYPE_INSTANCE_GET_PRIVATE (bistream,
65 SOUP_TYPE_BODY_INPUT_STREAM,
66 SoupBodyInputStreamPrivate);
67 bistream->priv->encoding = SOUP_ENCODING_NONE;
71 constructed (GObject *object)
73 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object);
75 bistream->priv->base_stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (bistream));
77 if (bistream->priv->encoding == SOUP_ENCODING_NONE ||
78 (bistream->priv->encoding == SOUP_ENCODING_CONTENT_LENGTH &&
79 bistream->priv->read_length == 0))
80 bistream->priv->eof = TRUE;
84 set_property (GObject *object, guint prop_id,
85 const GValue *value, GParamSpec *pspec)
87 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object);
91 bistream->priv->encoding = g_value_get_enum (value);
92 if (bistream->priv->encoding == SOUP_ENCODING_CHUNKED)
93 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE;
95 case PROP_CONTENT_LENGTH:
96 bistream->priv->read_length = g_value_get_int64 (value);
99 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
105 get_property (GObject *object, guint prop_id,
106 GValue *value, GParamSpec *pspec)
108 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object);
112 g_value_set_enum (value, bistream->priv->encoding);
115 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
121 soup_body_input_stream_read_raw (SoupBodyInputStream *bistream,
125 GCancellable *cancellable,
130 nread = g_pollable_stream_read (bistream->priv->base_stream,
135 bistream->priv->eof = TRUE;
136 if (bistream->priv->encoding != SOUP_ENCODING_EOF) {
137 g_set_error_literal (error, G_IO_ERROR,
138 G_IO_ERROR_PARTIAL_INPUT,
139 _("Connection terminated unexpectedly"));
147 soup_body_input_stream_read_chunked (SoupBodyInputStream *bistream,
151 GCancellable *cancellable,
154 SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (bistream->priv->base_stream);
160 switch (bistream->priv->chunked_state) {
161 case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE:
162 nread = soup_filter_input_stream_read_line (
163 fstream, metabuf, sizeof (metabuf), blocking,
164 &got_line, cancellable, error);
168 g_set_error_literal (error, G_IO_ERROR,
169 G_IO_ERROR_PARTIAL_INPUT,
170 _("Connection terminated unexpectedly"));
174 bistream->priv->read_length = strtoul (metabuf, NULL, 16);
175 if (bistream->priv->read_length > 0)
176 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK;
178 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_TRAILERS;
181 case SOUP_BODY_INPUT_STREAM_STATE_CHUNK:
182 nread = soup_body_input_stream_read_raw (
184 MIN (count, bistream->priv->read_length),
185 blocking, cancellable, error);
187 bistream->priv->read_length -= nread;
188 if (bistream->priv->read_length == 0)
189 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END;
193 case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END:
194 nread = soup_filter_input_stream_read_line (
195 SOUP_FILTER_INPUT_STREAM (bistream->priv->base_stream),
196 metabuf, sizeof (metabuf), blocking,
197 &got_line, cancellable, error);
201 g_set_error_literal (error, G_IO_ERROR,
202 G_IO_ERROR_PARTIAL_INPUT,
203 _("Connection terminated unexpectedly"));
207 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE;
210 case SOUP_BODY_INPUT_STREAM_STATE_TRAILERS:
211 nread = soup_filter_input_stream_read_line (
212 fstream, buffer, count, blocking,
213 &got_line, cancellable, error);
217 if (strncmp (buffer, "\r\n", nread) || strncmp (buffer, "\n", nread))
218 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_DONE;
221 case SOUP_BODY_INPUT_STREAM_STATE_DONE:
229 read_internal (GInputStream *stream,
233 GCancellable *cancellable,
236 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream);
239 if (bistream->priv->eof)
242 switch (bistream->priv->encoding) {
243 case SOUP_ENCODING_NONE:
246 case SOUP_ENCODING_CHUNKED:
247 return soup_body_input_stream_read_chunked (bistream, buffer, count,
248 blocking, cancellable, error);
250 case SOUP_ENCODING_CONTENT_LENGTH:
251 case SOUP_ENCODING_EOF:
252 if (bistream->priv->read_length != -1) {
253 count = MIN (count, bistream->priv->read_length);
258 nread = soup_body_input_stream_read_raw (bistream, buffer, count,
259 blocking, cancellable, error);
260 if (bistream->priv->read_length != -1 && nread > 0)
261 bistream->priv->read_length -= nread;
265 g_return_val_if_reached (-1);
270 soup_body_input_stream_read_fn (GInputStream *stream,
273 GCancellable *cancellable,
276 return read_internal (stream, buffer, count, TRUE,
281 soup_body_input_stream_close_fn (GInputStream *stream,
282 GCancellable *cancellable,
285 g_signal_emit (stream, signals[CLOSED], 0);
287 return G_INPUT_STREAM_CLASS (soup_body_input_stream_parent_class)->close_fn (stream, cancellable, error);
291 soup_body_input_stream_is_readable (GPollableInputStream *stream)
293 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream);
295 return bistream->priv->eof ||
296 g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (bistream->priv->base_stream));
300 soup_body_input_stream_read_nonblocking (GPollableInputStream *stream,
305 return read_internal (G_INPUT_STREAM (stream), buffer, count, FALSE,
310 soup_body_input_stream_create_source (GPollableInputStream *stream,
311 GCancellable *cancellable)
313 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream);
314 GSource *base_source, *pollable_source;
316 if (bistream->priv->eof)
317 base_source = g_timeout_source_new (0);
319 base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (bistream->priv->base_stream), cancellable);
320 g_source_set_dummy_callback (base_source);
322 pollable_source = g_pollable_source_new (G_OBJECT (stream));
323 g_source_add_child_source (pollable_source, base_source);
324 g_source_unref (base_source);
326 return pollable_source;
330 soup_body_input_stream_class_init (SoupBodyInputStreamClass *stream_class)
332 GObjectClass *object_class = G_OBJECT_CLASS (stream_class);
333 GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (stream_class);
335 g_type_class_add_private (stream_class, sizeof (SoupBodyInputStreamPrivate));
337 object_class->constructed = constructed;
338 object_class->set_property = set_property;
339 object_class->get_property = get_property;
341 input_stream_class->read_fn = soup_body_input_stream_read_fn;
342 input_stream_class->close_fn = soup_body_input_stream_close_fn;
345 g_signal_new ("closed",
346 G_OBJECT_CLASS_TYPE (object_class),
350 _soup_marshal_NONE__NONE,
353 g_object_class_install_property (
354 object_class, PROP_ENCODING,
355 g_param_spec_enum ("encoding",
357 "Message body encoding",
360 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
361 g_object_class_install_property (
362 object_class, PROP_CONTENT_LENGTH,
363 g_param_spec_int64 ("content-length",
365 "Message body Content-Length",
367 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
371 soup_body_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
372 gpointer interface_data)
374 pollable_interface->is_readable = soup_body_input_stream_is_readable;
375 pollable_interface->read_nonblocking = soup_body_input_stream_read_nonblocking;
376 pollable_interface->create_source = soup_body_input_stream_create_source;
380 soup_body_input_stream_new (SoupFilterInputStream *base_stream,
381 SoupEncoding encoding,
382 goffset content_length)
384 return g_object_new (SOUP_TYPE_BODY_INPUT_STREAM,
385 "base-stream", base_stream,
386 "close-base-stream", FALSE,
387 "encoding", encoding,
388 "content-length", content_length,