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_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
47 soup_body_output_stream_pollable_init))
51 soup_body_output_stream_init (SoupBodyOutputStream *stream)
53 stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
54 SOUP_TYPE_BODY_OUTPUT_STREAM,
55 SoupBodyOutputStreamPrivate);
59 soup_body_output_stream_constructed (GObject *object)
61 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
63 bostream->priv->base_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (bostream));
67 soup_body_output_stream_set_property (GObject *object, guint prop_id,
68 const GValue *value, GParamSpec *pspec)
70 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
74 bostream->priv->encoding = g_value_get_enum (value);
75 if (bostream->priv->encoding == SOUP_ENCODING_CHUNKED)
76 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE;
78 case PROP_CONTENT_LENGTH:
79 bostream->priv->write_length = g_value_get_uint64 (value);
82 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
88 soup_body_output_stream_get_property (GObject *object, guint prop_id,
89 GValue *value, GParamSpec *pspec)
91 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
95 g_value_set_enum (value, bostream->priv->encoding);
98 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
104 soup_body_output_stream_write_raw (SoupBodyOutputStream *bostream,
108 GCancellable *cancellable,
111 gssize nwrote, my_count;
113 /* If the caller tries to write too much to a Content-Length
114 * encoded stream, we truncate at the right point, but keep
115 * accepting additional data until they stop.
117 if (bostream->priv->write_length) {
118 my_count = MIN (count, bostream->priv->write_length - bostream->priv->written);
120 bostream->priv->eof = TRUE;
126 nwrote = g_pollable_stream_write (bostream->priv->base_stream,
128 blocking, cancellable, error);
130 if (nwrote > 0 && bostream->priv->write_length)
131 bostream->priv->written += nwrote;
133 if (nwrote == my_count && my_count != count)
140 soup_body_output_stream_write_chunked (SoupBodyOutputStream *bostream,
144 GCancellable *cancellable,
147 char *buf = bostream->priv->buf;
153 nwrote = g_pollable_stream_write (bostream->priv->base_stream,
158 memmove (buf, buf + nwrote, len + 1 - nwrote);
162 switch (bostream->priv->chunked_state) {
163 case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE:
164 g_snprintf (buf, sizeof (bostream->priv->buf),
165 "%lx\r\n", (gulong)count);
169 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK;
171 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_TRAILERS;
174 case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK:
175 nwrote = g_pollable_stream_write (bostream->priv->base_stream,
176 buffer, count, blocking,
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,
219 TRUE, cancellable, error);
222 return soup_body_output_stream_write_raw (bostream, buffer, count,
223 TRUE, cancellable, error);
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, TRUE, 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_write_nonblocking (GPollableOutputStream *stream,
257 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
259 if (bostream->priv->eof)
262 switch (bostream->priv->encoding) {
263 case SOUP_ENCODING_CHUNKED:
264 return soup_body_output_stream_write_chunked (bostream, buffer, count,
268 return soup_body_output_stream_write_raw (bostream, buffer, count,
274 soup_body_output_stream_create_source (GPollableOutputStream *stream,
275 GCancellable *cancellable)
277 SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
278 GSource *base_source, *pollable_source;
280 if (bostream->priv->eof)
281 base_source = g_timeout_source_new (0);
283 base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (bostream->priv->base_stream), cancellable);
284 g_source_set_dummy_callback (base_source);
286 pollable_source = g_pollable_source_new (G_OBJECT (stream));
287 g_source_add_child_source (pollable_source, base_source);
288 g_source_unref (base_source);
290 return pollable_source;
294 soup_body_output_stream_class_init (SoupBodyOutputStreamClass *stream_class)
296 GObjectClass *object_class = G_OBJECT_CLASS (stream_class);
297 GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (stream_class);
299 g_type_class_add_private (stream_class, sizeof (SoupBodyOutputStreamPrivate));
301 object_class->constructed = soup_body_output_stream_constructed;
302 object_class->set_property = soup_body_output_stream_set_property;
303 object_class->get_property = soup_body_output_stream_get_property;
305 output_stream_class->write_fn = soup_body_output_stream_write_fn;
306 output_stream_class->close_fn = soup_body_output_stream_close_fn;
308 g_object_class_install_property (
309 object_class, PROP_ENCODING,
310 g_param_spec_enum ("encoding",
312 "Message body encoding",
315 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
316 g_object_class_install_property (
317 object_class, PROP_CONTENT_LENGTH,
318 g_param_spec_uint64 ("content-length",
320 "Message body Content-Length",
322 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
326 soup_body_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_interface,
327 gpointer interface_data)
329 pollable_interface->is_writable = soup_body_output_stream_is_writable;
330 pollable_interface->write_nonblocking = soup_body_output_stream_write_nonblocking;
331 pollable_interface->create_source = soup_body_output_stream_create_source;
335 soup_body_output_stream_new (GOutputStream *base_stream,
336 SoupEncoding encoding,
337 goffset content_length)
339 return g_object_new (SOUP_TYPE_BODY_OUTPUT_STREAM,
340 "base-stream", base_stream,
341 "close-base-stream", FALSE,
342 "encoding", encoding,
343 "content-length", content_length,