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"
17 /* SoupConverterWrapper is a GConverter that wraps another GConverter.
18 * Mostly it is transparent, but it implements four special fallbacks
19 * for Content-Encoding handling: (1) "deflate" can mean either raw
20 * deflate or zlib-encoded deflate, (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, (4) the response may be truncated
24 * at an arbitrary point rather than containing a complete compressed
27 * If the wrapped conversion succeeds, then the wrapper will set the
28 * %SOUP_MESSAGE_CONTENT_DECODED flag on its message.
37 static void soup_converter_wrapper_iface_init (GConverterIface *iface);
39 G_DEFINE_TYPE_WITH_CODE (SoupConverterWrapper, soup_converter_wrapper, G_TYPE_OBJECT,
40 G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
41 soup_converter_wrapper_iface_init))
43 struct _SoupConverterWrapperPrivate
45 GConverter *base_converter;
47 gboolean try_deflate_fallback;
53 soup_converter_wrapper_init (SoupConverterWrapper *converter)
55 converter->priv = G_TYPE_INSTANCE_GET_PRIVATE (converter,
56 SOUP_TYPE_CONVERTER_WRAPPER,
57 SoupConverterWrapperPrivate);
61 soup_converter_wrapper_finalize (GObject *object)
63 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (object)->priv;
65 g_clear_object (&priv->base_converter);
66 g_clear_object (&priv->msg);
68 G_OBJECT_CLASS (soup_converter_wrapper_parent_class)->finalize (object);
73 soup_converter_wrapper_set_property (GObject *object,
78 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (object)->priv;
81 case PROP_BASE_CONVERTER:
82 priv->base_converter = g_value_dup_object (value);
83 if (G_IS_ZLIB_DECOMPRESSOR (priv->base_converter)) {
84 GZlibCompressorFormat format;
86 g_object_get (G_OBJECT (priv->base_converter),
89 if (format == G_ZLIB_COMPRESSOR_FORMAT_ZLIB)
90 priv->try_deflate_fallback = TRUE;
95 priv->msg = g_value_dup_object (value);
99 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
105 soup_converter_wrapper_get_property (GObject *object,
110 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (object)->priv;
113 case PROP_BASE_CONVERTER:
114 g_value_set_object (value, priv->base_converter);
118 g_value_set_object (value, priv->msg);
122 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
128 soup_converter_wrapper_class_init (SoupConverterWrapperClass *klass)
130 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
132 g_type_class_add_private (klass, sizeof (SoupConverterWrapperPrivate));
134 gobject_class->finalize = soup_converter_wrapper_finalize;
135 gobject_class->get_property = soup_converter_wrapper_get_property;
136 gobject_class->set_property = soup_converter_wrapper_set_property;
138 g_object_class_install_property (gobject_class,
140 g_param_spec_object ("base-converter",
142 "GConverter to wrap",
145 G_PARAM_CONSTRUCT_ONLY |
146 G_PARAM_STATIC_STRINGS));
147 g_object_class_install_property (gobject_class,
149 g_param_spec_object ("message",
151 "Associated SoupMessage",
154 G_PARAM_CONSTRUCT_ONLY |
155 G_PARAM_STATIC_STRINGS));
159 soup_converter_wrapper_new (GConverter *base_converter,
162 return g_object_new (SOUP_TYPE_CONVERTER_WRAPPER,
163 "base-converter", base_converter,
169 soup_converter_wrapper_reset (GConverter *converter)
171 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (converter)->priv;
173 if (priv->base_converter)
174 g_converter_reset (priv->base_converter);
177 static GConverterResult
178 soup_converter_wrapper_fallback_convert (GConverter *converter,
183 GConverterFlags flags,
185 gsize *bytes_written,
188 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (converter)->priv;
190 if (outbuf_size == 0) {
191 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
192 _("Output buffer is too small"));
193 return G_CONVERTER_ERROR;
196 if (priv->discarding) {
197 *bytes_read = inbuf_size;
199 } else if (outbuf_size >= inbuf_size) {
200 memcpy (outbuf, inbuf, inbuf_size);
201 *bytes_read = *bytes_written = inbuf_size;
203 memcpy (outbuf, inbuf, outbuf_size);
204 *bytes_read = *bytes_written = outbuf_size;
207 if (*bytes_read < inbuf_size)
208 return G_CONVERTER_CONVERTED;
210 if (flags & G_CONVERTER_INPUT_AT_END)
211 return G_CONVERTER_FINISHED;
212 else if (flags & G_CONVERTER_FLUSH)
213 return G_CONVERTER_FLUSHED;
215 return G_CONVERTER_CONVERTED;
217 /* Force it to either read more input or
218 * try again with G_CONVERTER_INPUT_AT_END.
220 g_set_error_literal (error, G_IO_ERROR,
221 G_IO_ERROR_PARTIAL_INPUT,
223 return G_CONVERTER_ERROR;
227 static GConverterResult
228 soup_converter_wrapper_real_convert (GConverter *converter,
233 GConverterFlags flags,
235 gsize *bytes_written,
238 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (converter)->priv;
239 GConverterResult result;
240 GError *my_error = NULL;
243 result = g_converter_convert (priv->base_converter,
246 flags, bytes_read, bytes_written,
248 if (result != G_CONVERTER_ERROR) {
249 if (!priv->started) {
250 SoupMessageFlags flags = soup_message_get_flags (priv->msg);
251 soup_message_set_flags (priv->msg, flags | SOUP_MESSAGE_CONTENT_DECODED);
252 priv->started = TRUE;
255 if (result == G_CONVERTER_FINISHED &&
256 !(flags & G_CONVERTER_INPUT_AT_END)) {
257 /* We need to keep reading (and discarding)
258 * input to the end of the message body.
260 g_clear_object (&priv->base_converter);
261 priv->discarding = TRUE;
264 return G_CONVERTER_CONVERTED;
266 g_set_error_literal (error, G_IO_ERROR,
267 G_IO_ERROR_PARTIAL_INPUT,
269 return G_CONVERTER_ERROR;
276 if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT) &&
277 inbuf_size == 0 && (flags & G_CONVERTER_INPUT_AT_END)) {
278 /* Server claimed compression but there was no message body. */
279 g_error_free (my_error);
281 return G_CONVERTER_FINISHED;
284 if (!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA) ||
286 g_propagate_error (error, my_error);
289 g_clear_error (&my_error);
291 /* Deflate hack: some servers (especially Apache with
292 * mod_deflate) return raw compressed data without the zlib
293 * headers when the client claims to support deflate.
295 if (priv->try_deflate_fallback) {
296 priv->try_deflate_fallback = FALSE;
297 g_object_unref (priv->base_converter);
298 priv->base_converter = (GConverter *)
299 g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_RAW);
303 /* Passthrough hack: some servers mistakenly claim to be
304 * sending encoded data when in fact they aren't, so fall
305 * back to just not decoding.
307 g_clear_object (&priv->base_converter);
308 return soup_converter_wrapper_fallback_convert (converter,
312 bytes_written, error);
315 static GConverterResult
316 soup_converter_wrapper_convert (GConverter *converter,
321 GConverterFlags flags,
323 gsize *bytes_written,
326 SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (converter)->priv;
328 if (priv->base_converter) {
329 return soup_converter_wrapper_real_convert (converter,
333 bytes_written, error);
335 return soup_converter_wrapper_fallback_convert (converter,
339 bytes_written, error);
344 soup_converter_wrapper_iface_init (GConverterIface *iface)
346 iface->convert = soup_converter_wrapper_convert;
347 iface->reset = soup_converter_wrapper_reset;