1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-converter-wrapper.c
5 * Copyright 2011 Red Hat, Inc.
12 #include <glib/gi18n-lib.h>
14 #include "soup-converter-wrapper.h"
15 #include "soup-message.h"
17 /* SoupConverterWrapper is a GConverter that wraps another GConverter.
18 * Mostly it is transparent, but it implements three special fallbacks
19 * for Content-Encoding handling: (1) "deflate" can mean either raw
20 * deflate or zlib-encoded default, (2) the server may mistakenly
21 * claim that a response is encoded when actually it isn't, (3) the
22 * response may contain trailing junk after the end of the encoded
23 * portion that we want to ignore.
25 * If the wrapped conversion succeeds, then the wrapper will set the
26 * %SOUP_MESSAGE_CONTENT_DECODED flag on its message.
35 static void soup_converter_wrapper_iface_init (GConverterIface *iface);
37 G_DEFINE_TYPE_WITH_CODE (SoupConverterWrapper, soup_converter_wrapper, G_TYPE_OBJECT,
38 G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
39 soup_converter_wrapper_iface_init))
41 struct _SoupConverterWrapperPrivate
43 GConverter *base_converter;
45 gboolean try_deflate_fallback;
51 soup_converter_wrapper_finalize (GObject *object)
53 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (object)->priv;
55 if (priv->base_converter)
56 g_object_unref (priv->base_converter);
58 G_OBJECT_CLASS (soup_converter_wrapper_parent_class)->finalize (object);
63 soup_converter_wrapper_set_property (GObject *object,
68 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (object)->priv;
71 case PROP_BASE_CONVERTER:
72 priv->base_converter = g_value_dup_object (value);
73 if (G_IS_ZLIB_DECOMPRESSOR (priv->base_converter)) {
74 GZlibCompressorFormat format;
76 g_object_get (G_OBJECT (priv->base_converter),
79 if (format == G_ZLIB_COMPRESSOR_FORMAT_ZLIB)
80 priv->try_deflate_fallback = TRUE;
85 priv->msg = g_value_dup_object (value);
89 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
95 soup_converter_wrapper_get_property (GObject *object,
100 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (object)->priv;
103 case PROP_BASE_CONVERTER:
104 g_value_set_object (value, priv->base_converter);
108 g_value_set_object (value, priv->msg);
112 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
118 soup_converter_wrapper_init (SoupConverterWrapper *converter)
120 converter->priv = G_TYPE_INSTANCE_GET_PRIVATE (converter,
121 SOUP_TYPE_CONVERTER_WRAPPER,
122 SoupConverterWrapperPrivate);
126 soup_converter_wrapper_class_init (SoupConverterWrapperClass *klass)
128 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
130 g_type_class_add_private (klass, sizeof (SoupConverterWrapperPrivate));
132 gobject_class->finalize = soup_converter_wrapper_finalize;
133 gobject_class->get_property = soup_converter_wrapper_get_property;
134 gobject_class->set_property = soup_converter_wrapper_set_property;
136 g_object_class_install_property (gobject_class,
138 g_param_spec_object ("base-converter",
140 "GConverter to wrap",
143 G_PARAM_CONSTRUCT_ONLY |
144 G_PARAM_STATIC_STRINGS));
145 g_object_class_install_property (gobject_class,
147 g_param_spec_object ("message",
149 "Associated SoupMessage",
152 G_PARAM_CONSTRUCT_ONLY |
153 G_PARAM_STATIC_STRINGS));
157 soup_converter_wrapper_new (GConverter *base_converter,
160 return g_object_new (SOUP_TYPE_CONVERTER_WRAPPER,
161 "base-converter", base_converter,
167 soup_converter_wrapper_reset (GConverter *converter)
169 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (converter)->priv;
171 if (priv->base_converter)
172 g_converter_reset (priv->base_converter);
175 static GConverterResult
176 soup_converter_wrapper_fallback_convert (GConverter *converter,
181 GConverterFlags flags,
183 gsize *bytes_written,
186 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (converter)->priv;
188 if (outbuf_size == 0) {
189 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
190 _("Output buffer is too small"));
191 return G_CONVERTER_ERROR;
194 if (priv->discarding) {
195 *bytes_read = inbuf_size;
197 } else if (outbuf_size >= inbuf_size) {
198 memcpy (outbuf, inbuf, inbuf_size);
199 *bytes_read = *bytes_written = inbuf_size;
201 memcpy (outbuf, inbuf, outbuf_size);
202 *bytes_read = *bytes_written = outbuf_size;
205 if (*bytes_read < inbuf_size)
206 return G_CONVERTER_CONVERTED;
208 if (flags & G_CONVERTER_INPUT_AT_END)
209 return G_CONVERTER_FINISHED;
210 else if (flags & G_CONVERTER_FLUSH)
211 return G_CONVERTER_FLUSHED;
213 return G_CONVERTER_CONVERTED;
215 /* Force it to either read more input or
216 * try again with G_CONVERTER_INPUT_AT_END.
218 g_set_error_literal (error, G_IO_ERROR,
219 G_IO_ERROR_PARTIAL_INPUT,
221 return G_CONVERTER_ERROR;
225 static GConverterResult
226 soup_converter_wrapper_real_convert (GConverter *converter,
231 GConverterFlags flags,
233 gsize *bytes_written,
236 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (converter)->priv;
237 GConverterResult result;
238 GError *my_error = NULL;
241 result = g_converter_convert (priv->base_converter,
244 flags, bytes_read, bytes_written,
246 if (result != G_CONVERTER_ERROR) {
247 if (!priv->started) {
248 SoupMessageFlags flags = soup_message_get_flags (priv->msg);
249 soup_message_set_flags (priv->msg, flags | SOUP_MESSAGE_CONTENT_DECODED);
250 priv->started = TRUE;
253 if (result == G_CONVERTER_FINISHED &&
254 !(flags & G_CONVERTER_INPUT_AT_END)) {
255 /* We need to keep reading (and discarding)
256 * input to the end of the message body.
258 g_clear_object (&priv->base_converter);
259 priv->discarding = TRUE;
262 return G_CONVERTER_CONVERTED;
264 g_set_error_literal (error, G_IO_ERROR,
265 G_IO_ERROR_PARTIAL_INPUT,
267 return G_CONVERTER_ERROR;
274 if (!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA) ||
276 g_propagate_error (error, my_error);
279 g_clear_error (&my_error);
281 /* Deflate hack: some servers (especially Apache with
282 * mod_deflate) return raw compressed data without the zlib
283 * headers when the client claims to support deflate.
285 if (priv->try_deflate_fallback) {
286 priv->try_deflate_fallback = FALSE;
287 g_object_unref (priv->base_converter);
288 priv->base_converter = (GConverter *)
289 g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_RAW);
293 /* Passthrough hack: some servers mistakenly claim to be
294 * sending encoded data when in fact they aren't, so fall
295 * back to just not decoding.
297 g_clear_object (&priv->base_converter);
298 return soup_converter_wrapper_fallback_convert (converter,
302 bytes_written, error);
305 static GConverterResult
306 soup_converter_wrapper_convert (GConverter *converter,
311 GConverterFlags flags,
313 gsize *bytes_written,
316 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (converter)->priv;
318 if (priv->base_converter) {
319 return soup_converter_wrapper_real_convert (converter,
323 bytes_written, error);
325 return soup_converter_wrapper_fallback_convert (converter,
329 bytes_written, error);
334 soup_converter_wrapper_iface_init (GConverterIface *iface)
336 iface->convert = soup_converter_wrapper_convert;
337 iface->reset = soup_converter_wrapper_reset;