fixed typo enable_sqllite -> enable_sqlite
[platform/upstream/libsoup.git] / libsoup / soup-body-output-stream.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-body-output-stream.c
4  *
5  * Copyright 2012 Red Hat, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <string.h>
13
14 #include "soup-body-output-stream.h"
15 #include "soup.h"
16
17 typedef enum {
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;
24
25 struct _SoupBodyOutputStreamPrivate {
26         GOutputStream *base_stream;
27         char           buf[20];
28
29         SoupEncoding   encoding;
30         goffset        write_length;
31         goffset        written;
32         SoupBodyOutputStreamState chunked_state;
33         gboolean       eof;
34 };
35
36 enum {
37         PROP_0,
38
39         PROP_ENCODING,
40         PROP_CONTENT_LENGTH
41 };
42
43 static void soup_body_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_interface, gpointer interface_data);
44
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))
48
49
50 static void
51 soup_body_output_stream_init (SoupBodyOutputStream *stream)
52 {
53         stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
54                                                     SOUP_TYPE_BODY_OUTPUT_STREAM,
55                                                     SoupBodyOutputStreamPrivate);
56 }
57
58 static void
59 soup_body_output_stream_constructed (GObject *object)
60 {
61         SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
62
63         bostream->priv->base_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (bostream));
64 }
65
66 static void
67 soup_body_output_stream_set_property (GObject *object, guint prop_id,
68                                       const GValue *value, GParamSpec *pspec)
69 {
70         SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
71
72         switch (prop_id) {
73         case PROP_ENCODING:
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;
77                 break;
78         case PROP_CONTENT_LENGTH:
79                 bostream->priv->write_length = g_value_get_uint64 (value);
80                 break;
81         default:
82                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
83                 break;
84         }
85 }
86
87 static void
88 soup_body_output_stream_get_property (GObject *object, guint prop_id,
89                                       GValue *value, GParamSpec *pspec)
90 {
91         SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
92
93         switch (prop_id) {
94         case PROP_ENCODING:
95                 g_value_set_enum (value, bostream->priv->encoding);
96                 break;
97         default:
98                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
99                 break;
100         }
101 }
102
103 static gssize
104 soup_body_output_stream_write_raw (SoupBodyOutputStream  *bostream,
105                                    const void            *buffer,
106                                    gsize                  count,
107                                    gboolean               blocking,
108                                    GCancellable          *cancellable,
109                                    GError               **error)
110 {
111         gssize nwrote, my_count;
112
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.
116          */
117         if (bostream->priv->write_length) {
118                 my_count = MIN (count, bostream->priv->write_length - bostream->priv->written);
119                 if (my_count == 0) {
120                         bostream->priv->eof = TRUE;
121                         return count;
122                 }
123         } else
124                 my_count = count;
125
126         nwrote = g_pollable_stream_write (bostream->priv->base_stream,
127                                           buffer, my_count,
128                                           blocking, cancellable, error);
129
130         if (nwrote > 0 && bostream->priv->write_length)
131                 bostream->priv->written += nwrote;
132
133         if (nwrote == my_count && my_count != count)
134                 nwrote = count;
135
136         return nwrote;
137 }
138
139 static gssize
140 soup_body_output_stream_write_chunked (SoupBodyOutputStream  *bostream,
141                                        const void            *buffer,
142                                        gsize                  count,
143                                        gboolean               blocking,
144                                        GCancellable          *cancellable,
145                                        GError               **error)
146 {
147         char *buf = bostream->priv->buf;
148         gssize nwrote, len;
149
150 again:
151         len = strlen (buf);
152         if (len) {
153                 nwrote = g_pollable_stream_write (bostream->priv->base_stream,
154                                                   buf, len, blocking,
155                                                   cancellable, error);
156                 if (nwrote < 0)
157                         return nwrote;
158                 memmove (buf, buf + nwrote, len + 1 - nwrote);
159                 goto again;
160         }
161
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);
166                 len = strlen (buf);
167
168                 if (count > 0)
169                         bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK;
170                 else
171                         bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_TRAILERS;
172                 break;
173
174         case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK:
175                 nwrote = g_pollable_stream_write (bostream->priv->base_stream,
176                                                   buffer, count, blocking,
177                                                   cancellable, error);
178                 if (nwrote < (gssize)count)
179                         return nwrote;
180
181                 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_END;
182                 break;
183
184         case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_END:
185                 strncpy (buf, "\r\n", sizeof (bostream->priv->buf));
186                 len = 2;
187                 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_DONE;
188                 break;
189
190         case SOUP_BODY_OUTPUT_STREAM_STATE_TRAILERS:
191                 strncpy (buf, "\r\n", sizeof (bostream->priv->buf));
192                 len = 2;
193                 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_DONE;
194                 break;
195
196         case SOUP_BODY_OUTPUT_STREAM_STATE_DONE:
197                 bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE;
198                 return count;
199         }
200
201         goto again;
202 }
203
204 static gssize
205 soup_body_output_stream_write_fn (GOutputStream  *stream,
206                                   const void     *buffer,
207                                   gsize           count,
208                                   GCancellable   *cancellable,
209                                   GError        **error)
210 {
211         SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
212
213         if (bostream->priv->eof)
214                 return count;
215
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);
220
221         default:
222                 return soup_body_output_stream_write_raw (bostream, buffer, count,
223                                                           TRUE, cancellable, error);
224         }
225 }
226
227 static gboolean
228 soup_body_output_stream_close_fn (GOutputStream  *stream,
229                                   GCancellable   *cancellable,
230                                   GError        **error)
231 {
232         SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
233
234         if (bostream->priv->encoding == SOUP_ENCODING_CHUNKED &&
235             bostream->priv->chunked_state == SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE) {
236                 if (soup_body_output_stream_write_chunked (bostream, NULL, 0, TRUE, cancellable, error) == -1)
237                         return FALSE;
238         }
239
240         return G_OUTPUT_STREAM_CLASS (soup_body_output_stream_parent_class)->close_fn (stream, cancellable, error);
241 }
242
243 static gboolean
244 soup_body_output_stream_is_writable (GPollableOutputStream *stream)
245 {
246         SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
247
248         return bostream->priv->eof ||
249                 g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (bostream->priv->base_stream));
250 }
251
252 static gssize
253 soup_body_output_stream_write_nonblocking (GPollableOutputStream  *stream,
254                                            const void             *buffer,
255                                            gsize                   count,
256                                            GError                **error)
257 {
258         SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
259
260         if (bostream->priv->eof)
261                 return count;
262
263         switch (bostream->priv->encoding) {
264         case SOUP_ENCODING_CHUNKED:
265                 return soup_body_output_stream_write_chunked (bostream, buffer, count,
266                                                               FALSE, NULL, error);
267
268         default:
269                 return soup_body_output_stream_write_raw (bostream, buffer, count,
270                                                           FALSE, NULL, error);
271         }
272 }
273
274 static GSource *
275 soup_body_output_stream_create_source (GPollableOutputStream *stream,
276                                        GCancellable *cancellable)
277 {
278         SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
279         GSource *base_source, *pollable_source;
280
281         if (bostream->priv->eof)
282                 base_source = g_timeout_source_new (0);
283         else
284                 base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (bostream->priv->base_stream), cancellable);
285         g_source_set_dummy_callback (base_source);
286
287         pollable_source = g_pollable_source_new (G_OBJECT (stream));
288         g_source_add_child_source (pollable_source, base_source);
289         g_source_unref (base_source);
290
291         return pollable_source;
292 }
293
294 static void
295 soup_body_output_stream_class_init (SoupBodyOutputStreamClass *stream_class)
296 {
297         GObjectClass *object_class = G_OBJECT_CLASS (stream_class);
298         GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (stream_class);
299
300         g_type_class_add_private (stream_class, sizeof (SoupBodyOutputStreamPrivate));
301
302         object_class->constructed = soup_body_output_stream_constructed;
303         object_class->set_property = soup_body_output_stream_set_property;
304         object_class->get_property = soup_body_output_stream_get_property;
305
306         output_stream_class->write_fn = soup_body_output_stream_write_fn;
307         output_stream_class->close_fn = soup_body_output_stream_close_fn;
308
309         g_object_class_install_property (
310                 object_class, PROP_ENCODING,
311                 g_param_spec_enum ("encoding",
312                                    "Encoding",
313                                    "Message body encoding",
314                                    SOUP_TYPE_ENCODING,
315                                    SOUP_ENCODING_NONE,
316                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
317         g_object_class_install_property (
318                 object_class, PROP_CONTENT_LENGTH,
319                 g_param_spec_uint64 ("content-length",
320                                      "Content-Length",
321                                      "Message body Content-Length",
322                                      0, G_MAXUINT64, 0,
323                                      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
324 }
325
326 static void
327 soup_body_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_interface,
328                                        gpointer interface_data)
329 {
330         pollable_interface->is_writable = soup_body_output_stream_is_writable;
331         pollable_interface->write_nonblocking = soup_body_output_stream_write_nonblocking;
332         pollable_interface->create_source = soup_body_output_stream_create_source;
333 }
334
335 GOutputStream *
336 soup_body_output_stream_new (GOutputStream *base_stream,
337                              SoupEncoding   encoding,
338                              goffset        content_length)
339 {
340         return g_object_new (SOUP_TYPE_BODY_OUTPUT_STREAM,
341                              "base-stream", base_stream,
342                              "close-base-stream", FALSE,
343                              "encoding", encoding,
344                              "content-length", content_length,
345                              NULL);
346 }