1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-body-output-stream.c
5 * Copyright 2012 Red Hat, Inc.
17 #include "soup-body-output-stream.h"
18 #include "soup-enum-types.h"
19 #include "soup-message-headers.h"
22 SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE,
23 SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_END,
24 SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK,
25 SOUP_BODY_OUTPUT_STREAM_STATE_TRAILERS,
26 SOUP_BODY_OUTPUT_STREAM_STATE_DONE
27 } SoupBodyOutputStreamState;
29 struct _SoupBodyOutputStreamPrivate {
30 GOutputStream *base_stream;
33 SoupEncoding encoding;
36 SoupBodyOutputStreamState chunked_state;
47 static void soup_body_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_interface, gpointer interface_data);
49 G_DEFINE_TYPE_WITH_CODE (SoupBodyOutputStream, soup_body_output_stream, G_TYPE_FILTER_OUTPUT_STREAM,
50 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
51 soup_body_output_stream_pollable_init))
55 soup_body_output_stream_init (SoupBodyOutputStream *stream)
57 stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
58 SOUP_TYPE_BODY_OUTPUT_STREAM,
59 SoupBodyOutputStreamPrivate);
63 constructed (GObject *object)
65 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
67 bostream->priv->base_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (bostream));
71 set_property (GObject *object, guint prop_id,
72 const GValue *value, GParamSpec *pspec)
74 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
78 bostream->priv->encoding = g_value_get_enum (value);
79 if (bostream->priv->encoding == SOUP_ENCODING_CHUNKED)
80 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE;
82 case PROP_CONTENT_LENGTH:
83 bostream->priv->write_length = g_value_get_uint64 (value);
86 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
92 get_property (GObject *object, guint prop_id,
93 GValue *value, GParamSpec *pspec)
95 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
99 g_value_set_enum (value, bostream->priv->encoding);
102 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
108 soup_body_output_stream_write_raw (SoupBodyOutputStream *bostream,
111 GCancellable *cancellable,
114 gssize nwrote, my_count;
116 /* If the caller tries to write too much to a Content-Length
117 * encoded stream, we truncate at the right point, but keep
118 * accepting additional data until they stop.
120 if (bostream->priv->write_length) {
121 my_count = MIN (count, bostream->priv->write_length - bostream->priv->written);
123 bostream->priv->eof = TRUE;
129 nwrote = g_output_stream_write (bostream->priv->base_stream,
133 if (nwrote > 0 && bostream->priv->write_length)
134 bostream->priv->written += nwrote;
136 if (nwrote == my_count && my_count != count)
143 soup_body_output_stream_write_chunked (SoupBodyOutputStream *bostream,
146 GCancellable *cancellable,
149 char *buf = bostream->priv->buf;
155 nwrote = g_output_stream_write (bostream->priv->base_stream,
156 buf, len, cancellable, error);
159 memmove (buf, buf + nwrote, len + 1 - nwrote);
163 switch (bostream->priv->chunked_state) {
164 case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE:
165 snprintf (buf, sizeof (bostream->priv->buf),
166 "%lx\r\n", (gulong)count);
170 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK;
172 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_TRAILERS;
175 case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK:
176 nwrote = g_output_stream_write (bostream->priv->base_stream,
177 buffer, count, cancellable, error);
178 if (nwrote < (gssize)count)
181 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_END;
184 case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_END:
185 strncpy (buf, "\r\n", sizeof (bostream->priv->buf));
187 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_DONE;
190 case SOUP_BODY_OUTPUT_STREAM_STATE_TRAILERS:
191 strncpy (buf, "\r\n", sizeof (bostream->priv->buf));
193 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_DONE;
196 case SOUP_BODY_OUTPUT_STREAM_STATE_DONE:
197 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE;
205 soup_body_output_stream_write_fn (GOutputStream *stream,
208 GCancellable *cancellable,
211 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
213 if (bostream->priv->eof)
216 switch (bostream->priv->encoding) {
217 case SOUP_ENCODING_CHUNKED:
218 return soup_body_output_stream_write_chunked (bostream, buffer, count,
222 return soup_body_output_stream_write_raw (bostream, buffer, count,
228 soup_body_output_stream_close_fn (GOutputStream *stream,
229 GCancellable *cancellable,
232 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
234 if (bostream->priv->encoding == SOUP_ENCODING_CHUNKED) {
235 if (soup_body_output_stream_write_chunked (bostream, NULL, 0, cancellable, error) == -1)
239 return G_OUTPUT_STREAM_CLASS (soup_body_output_stream_parent_class)->close_fn (stream, cancellable, error);
243 soup_body_output_stream_is_writable (GPollableOutputStream *stream)
245 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
247 return bostream->priv->eof ||
248 g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (bostream->priv->base_stream));
252 soup_body_output_stream_create_source (GPollableOutputStream *stream,
253 GCancellable *cancellable)
255 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
256 GSource *base_source, *pollable_source;
258 if (bostream->priv->eof)
259 base_source = g_timeout_source_new (0);
261 base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (bostream->priv->base_stream), cancellable);
262 g_source_set_dummy_callback (base_source);
264 pollable_source = g_pollable_source_new (G_OBJECT (stream));
265 g_source_add_child_source (pollable_source, base_source);
266 g_source_unref (base_source);
268 return pollable_source;
272 soup_body_output_stream_class_init (SoupBodyOutputStreamClass *stream_class)
274 GObjectClass *object_class = G_OBJECT_CLASS (stream_class);
275 GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (stream_class);
277 g_type_class_add_private (stream_class, sizeof (SoupBodyOutputStreamPrivate));
279 object_class->constructed = constructed;
280 object_class->set_property = set_property;
281 object_class->get_property = get_property;
283 output_stream_class->write_fn = soup_body_output_stream_write_fn;
284 output_stream_class->close_fn = soup_body_output_stream_close_fn;
286 g_object_class_install_property (
287 object_class, PROP_ENCODING,
288 g_param_spec_enum ("encoding",
290 "Message body encoding",
293 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
294 g_object_class_install_property (
295 object_class, PROP_CONTENT_LENGTH,
296 g_param_spec_uint64 ("content-length",
298 "Message body Content-Length",
300 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
304 soup_body_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_interface,
305 gpointer interface_data)
307 pollable_interface->is_writable = soup_body_output_stream_is_writable;
308 pollable_interface->create_source = soup_body_output_stream_create_source;
312 soup_body_output_stream_new (GOutputStream *base_stream,
313 SoupEncoding encoding,
314 goffset content_length)
316 return g_object_new (SOUP_TYPE_BODY_OUTPUT_STREAM,
317 "base-stream", base_stream,
318 "close-base-stream", FALSE,
319 "encoding", encoding,
320 "content-length", content_length,