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.
14 #include "soup-body-output-stream.h"
18 SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE,
19 SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_END,
20 SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK,
21 SOUP_BODY_OUTPUT_STREAM_STATE_TRAILERS,
22 SOUP_BODY_OUTPUT_STREAM_STATE_DONE
23 } SoupBodyOutputStreamState;
25 struct _SoupBodyOutputStreamPrivate {
26 GOutputStream *base_stream;
29 SoupEncoding encoding;
32 SoupBodyOutputStreamState chunked_state;
43 static void soup_body_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_interface, gpointer interface_data);
45 G_DEFINE_TYPE_WITH_CODE (SoupBodyOutputStream, soup_body_output_stream, G_TYPE_FILTER_OUTPUT_STREAM,
46 G_ADD_PRIVATE (SoupBodyOutputStream)
47 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
48 soup_body_output_stream_pollable_init))
52 soup_body_output_stream_init (SoupBodyOutputStream *stream)
54 stream->priv = soup_body_output_stream_get_instance_private (stream);
58 soup_body_output_stream_constructed (GObject *object)
60 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
62 bostream->priv->base_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (bostream));
66 soup_body_output_stream_set_property (GObject *object, guint prop_id,
67 const GValue *value, GParamSpec *pspec)
69 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
73 bostream->priv->encoding = g_value_get_enum (value);
74 if (bostream->priv->encoding == SOUP_ENCODING_CHUNKED)
75 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE;
77 case PROP_CONTENT_LENGTH:
78 bostream->priv->write_length = g_value_get_uint64 (value);
81 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
87 soup_body_output_stream_get_property (GObject *object, guint prop_id,
88 GValue *value, GParamSpec *pspec)
90 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
94 g_value_set_enum (value, bostream->priv->encoding);
97 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
103 soup_body_output_stream_write_raw (SoupBodyOutputStream *bostream,
107 GCancellable *cancellable,
110 gssize nwrote, my_count;
112 /* If the caller tries to write too much to a Content-Length
113 * encoded stream, we truncate at the right point, but keep
114 * accepting additional data until they stop.
116 if (bostream->priv->write_length) {
117 my_count = MIN (count, bostream->priv->write_length - bostream->priv->written);
119 bostream->priv->eof = TRUE;
125 nwrote = g_pollable_stream_write (bostream->priv->base_stream,
127 blocking, cancellable, error);
129 if (nwrote > 0 && bostream->priv->write_length)
130 bostream->priv->written += nwrote;
132 if (nwrote == my_count && my_count != count)
139 soup_body_output_stream_write_chunked (SoupBodyOutputStream *bostream,
143 GCancellable *cancellable,
146 char *buf = bostream->priv->buf;
152 nwrote = g_pollable_stream_write (bostream->priv->base_stream,
157 memmove (buf, buf + nwrote, len + 1 - nwrote);
161 switch (bostream->priv->chunked_state) {
162 case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE:
163 g_snprintf (buf, sizeof (bostream->priv->buf),
164 "%lx\r\n", (gulong)count);
167 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK;
169 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_TRAILERS;
172 case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK:
173 nwrote = g_pollable_stream_write (bostream->priv->base_stream,
174 buffer, count, blocking,
176 if (nwrote < (gssize)count)
179 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_END;
182 case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_END:
183 strncpy (buf, "\r\n", sizeof (bostream->priv->buf));
184 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_DONE;
187 case SOUP_BODY_OUTPUT_STREAM_STATE_TRAILERS:
188 strncpy (buf, "\r\n", sizeof (bostream->priv->buf));
189 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_DONE;
192 case SOUP_BODY_OUTPUT_STREAM_STATE_DONE:
193 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE;
201 soup_body_output_stream_write_fn (GOutputStream *stream,
204 GCancellable *cancellable,
207 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
209 if (bostream->priv->eof)
212 switch (bostream->priv->encoding) {
213 case SOUP_ENCODING_CHUNKED:
214 return soup_body_output_stream_write_chunked (bostream, buffer, count,
215 TRUE, cancellable, error);
218 return soup_body_output_stream_write_raw (bostream, buffer, count,
219 TRUE, cancellable, error);
224 soup_body_output_stream_close_fn (GOutputStream *stream,
225 GCancellable *cancellable,
228 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
230 if (bostream->priv->encoding == SOUP_ENCODING_CHUNKED &&
231 bostream->priv->chunked_state == SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE) {
232 if (soup_body_output_stream_write_chunked (bostream, NULL, 0, TRUE, cancellable, error) == -1)
236 return G_OUTPUT_STREAM_CLASS (soup_body_output_stream_parent_class)->close_fn (stream, cancellable, error);
240 soup_body_output_stream_is_writable (GPollableOutputStream *stream)
242 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
244 return bostream->priv->eof ||
245 g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (bostream->priv->base_stream));
249 soup_body_output_stream_write_nonblocking (GPollableOutputStream *stream,
254 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
256 if (bostream->priv->eof)
259 switch (bostream->priv->encoding) {
260 case SOUP_ENCODING_CHUNKED:
261 return soup_body_output_stream_write_chunked (bostream, buffer, count,
265 return soup_body_output_stream_write_raw (bostream, buffer, count,
271 soup_body_output_stream_create_source (GPollableOutputStream *stream,
272 GCancellable *cancellable)
274 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
275 GSource *base_source, *pollable_source;
277 if (bostream->priv->eof)
278 base_source = g_timeout_source_new (0);
280 base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (bostream->priv->base_stream), cancellable);
281 g_source_set_dummy_callback (base_source);
283 pollable_source = g_pollable_source_new (G_OBJECT (stream));
284 g_source_add_child_source (pollable_source, base_source);
285 g_source_unref (base_source);
287 return pollable_source;
291 soup_body_output_stream_class_init (SoupBodyOutputStreamClass *stream_class)
293 GObjectClass *object_class = G_OBJECT_CLASS (stream_class);
294 GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (stream_class);
296 object_class->constructed = soup_body_output_stream_constructed;
297 object_class->set_property = soup_body_output_stream_set_property;
298 object_class->get_property = soup_body_output_stream_get_property;
300 output_stream_class->write_fn = soup_body_output_stream_write_fn;
301 output_stream_class->close_fn = soup_body_output_stream_close_fn;
303 g_object_class_install_property (
304 object_class, PROP_ENCODING,
305 g_param_spec_enum ("encoding",
307 "Message body encoding",
310 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
311 G_PARAM_STATIC_STRINGS));
312 g_object_class_install_property (
313 object_class, PROP_CONTENT_LENGTH,
314 g_param_spec_uint64 ("content-length",
316 "Message body Content-Length",
318 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
319 G_PARAM_STATIC_STRINGS));
323 soup_body_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_interface,
324 gpointer interface_data)
326 pollable_interface->is_writable = soup_body_output_stream_is_writable;
327 pollable_interface->write_nonblocking = soup_body_output_stream_write_nonblocking;
328 pollable_interface->create_source = soup_body_output_stream_create_source;
332 soup_body_output_stream_new (GOutputStream *base_stream,
333 SoupEncoding encoding,
334 goffset content_length)
336 return g_object_new (SOUP_TYPE_BODY_OUTPUT_STREAM,
337 "base-stream", base_stream,
338 "close-base-stream", FALSE,
339 "encoding", encoding,
340 "content-length", content_length,