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-message-headers.h"
24 SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE,
25 SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END,
26 SOUP_BODY_INPUT_STREAM_STATE_CHUNK,
27 SOUP_BODY_INPUT_STREAM_STATE_TRAILERS,
28 SOUP_BODY_INPUT_STREAM_STATE_DONE
29 } SoupBodyInputStreamState;
31 struct _SoupBodyInputStreamPrivate {
32 GInputStream *base_stream;
34 SoupEncoding encoding;
36 SoupBodyInputStreamState chunked_state;
47 static void soup_body_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
49 G_DEFINE_TYPE_WITH_CODE (SoupBodyInputStream, soup_body_input_stream, G_TYPE_FILTER_INPUT_STREAM,
50 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
51 soup_body_input_stream_pollable_init))
54 soup_body_input_stream_init (SoupBodyInputStream *bistream)
56 bistream->priv = G_TYPE_INSTANCE_GET_PRIVATE (bistream,
57 SOUP_TYPE_BODY_INPUT_STREAM,
58 SoupBodyInputStreamPrivate);
59 bistream->priv->encoding = SOUP_ENCODING_NONE;
63 constructed (GObject *object)
65 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object);
67 bistream->priv->base_stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (bistream));
69 if (bistream->priv->encoding == SOUP_ENCODING_NONE ||
70 (bistream->priv->encoding == SOUP_ENCODING_CONTENT_LENGTH &&
71 bistream->priv->read_length == 0))
72 bistream->priv->eof = TRUE;
76 set_property (GObject *object, guint prop_id,
77 const GValue *value, GParamSpec *pspec)
79 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object);
83 bistream->priv->encoding = g_value_get_enum (value);
84 if (bistream->priv->encoding == SOUP_ENCODING_CHUNKED)
85 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE;
87 case PROP_CONTENT_LENGTH:
88 bistream->priv->read_length = g_value_get_int64 (value);
91 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
97 get_property (GObject *object, guint prop_id,
98 GValue *value, GParamSpec *pspec)
100 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object);
104 g_value_set_enum (value, bistream->priv->encoding);
107 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
113 soup_body_input_stream_read_raw (SoupBodyInputStream *bistream,
117 GCancellable *cancellable,
122 nread = g_pollable_stream_read (bistream->priv->base_stream,
127 bistream->priv->eof = TRUE;
128 if (bistream->priv->encoding != SOUP_ENCODING_EOF) {
129 g_set_error_literal (error, G_IO_ERROR,
130 G_IO_ERROR_PARTIAL_INPUT,
131 _("Connection terminated unexpectedly"));
139 soup_body_input_stream_read_chunked (SoupBodyInputStream *bistream,
143 GCancellable *cancellable,
146 SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (bistream->priv->base_stream);
152 switch (bistream->priv->chunked_state) {
153 case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE:
154 nread = soup_filter_input_stream_read_line (
155 fstream, metabuf, sizeof (metabuf), blocking,
156 &got_line, cancellable, error);
160 g_set_error_literal (error, G_IO_ERROR,
161 G_IO_ERROR_PARTIAL_INPUT,
162 _("Connection terminated unexpectedly"));
166 bistream->priv->read_length = strtoul (metabuf, NULL, 16);
167 if (bistream->priv->read_length > 0)
168 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK;
170 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_TRAILERS;
173 case SOUP_BODY_INPUT_STREAM_STATE_CHUNK:
174 nread = soup_body_input_stream_read_raw (
176 MIN (count, bistream->priv->read_length),
177 blocking, cancellable, error);
179 bistream->priv->read_length -= nread;
180 if (bistream->priv->read_length == 0)
181 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END;
185 case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END:
186 nread = soup_filter_input_stream_read_line (
187 SOUP_FILTER_INPUT_STREAM (bistream->priv->base_stream),
188 metabuf, sizeof (metabuf), blocking,
189 &got_line, cancellable, error);
193 g_set_error_literal (error, G_IO_ERROR,
194 G_IO_ERROR_PARTIAL_INPUT,
195 _("Connection terminated unexpectedly"));
199 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE;
202 case SOUP_BODY_INPUT_STREAM_STATE_TRAILERS:
203 nread = soup_filter_input_stream_read_line (
204 fstream, buffer, count, blocking,
205 &got_line, cancellable, error);
209 if (strncmp (buffer, "\r\n", nread) || strncmp (buffer, "\n", nread))
210 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_DONE;
213 case SOUP_BODY_INPUT_STREAM_STATE_DONE:
221 read_internal (GInputStream *stream,
225 GCancellable *cancellable,
228 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream);
231 if (bistream->priv->eof)
234 switch (bistream->priv->encoding) {
235 case SOUP_ENCODING_NONE:
238 case SOUP_ENCODING_CHUNKED:
239 return soup_body_input_stream_read_chunked (bistream, buffer, count,
240 blocking, cancellable, error);
242 case SOUP_ENCODING_CONTENT_LENGTH:
243 case SOUP_ENCODING_EOF:
244 if (bistream->priv->read_length != -1) {
245 count = MIN (count, bistream->priv->read_length);
250 nread = soup_body_input_stream_read_raw (bistream, buffer, count,
251 blocking, cancellable, error);
252 if (bistream->priv->read_length != -1 && nread > 0)
253 bistream->priv->read_length -= nread;
257 g_return_val_if_reached (-1);
262 soup_body_input_stream_read_fn (GInputStream *stream,
265 GCancellable *cancellable,
268 return read_internal (stream, buffer, count, TRUE,
273 soup_body_input_stream_is_readable (GPollableInputStream *stream)
275 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream);
277 return bistream->priv->eof ||
278 g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (bistream->priv->base_stream));
282 soup_body_input_stream_read_nonblocking (GPollableInputStream *stream,
287 return read_internal (G_INPUT_STREAM (stream), buffer, count, FALSE,
292 soup_body_input_stream_create_source (GPollableInputStream *stream,
293 GCancellable *cancellable)
295 SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream);
296 GSource *base_source, *pollable_source;
298 if (bistream->priv->eof)
299 base_source = g_timeout_source_new (0);
301 base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (bistream->priv->base_stream), cancellable);
302 g_source_set_dummy_callback (base_source);
304 pollable_source = g_pollable_source_new (G_OBJECT (stream));
305 g_source_add_child_source (pollable_source, base_source);
306 g_source_unref (base_source);
308 return pollable_source;
312 soup_body_input_stream_class_init (SoupBodyInputStreamClass *stream_class)
314 GObjectClass *object_class = G_OBJECT_CLASS (stream_class);
315 GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (stream_class);
317 g_type_class_add_private (stream_class, sizeof (SoupBodyInputStreamPrivate));
319 object_class->constructed = constructed;
320 object_class->set_property = set_property;
321 object_class->get_property = get_property;
323 input_stream_class->read_fn = soup_body_input_stream_read_fn;
325 g_object_class_install_property (
326 object_class, PROP_ENCODING,
327 g_param_spec_enum ("encoding",
329 "Message body encoding",
332 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
333 g_object_class_install_property (
334 object_class, PROP_CONTENT_LENGTH,
335 g_param_spec_int64 ("content-length",
337 "Message body Content-Length",
339 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
343 soup_body_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
344 gpointer interface_data)
346 pollable_interface->is_readable = soup_body_input_stream_is_readable;
347 pollable_interface->read_nonblocking = soup_body_input_stream_read_nonblocking;
348 pollable_interface->create_source = soup_body_input_stream_create_source;
352 soup_body_input_stream_new (SoupFilterInputStream *base_stream,
353 SoupEncoding encoding,
354 goffset content_length)
356 return g_object_new (SOUP_TYPE_BODY_INPUT_STREAM,
357 "base-stream", base_stream,
358 "close-base-stream", FALSE,
359 "encoding", encoding,
360 "content-length", content_length,