1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-content-sniffer-stream.c
5 * Copyright (C) 2010 Red Hat, Inc.
15 #include "soup-content-sniffer-stream.h"
16 #include "soup-content-sniffer.h"
17 #include "soup-message.h"
19 static void soup_content_sniffer_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
21 G_DEFINE_TYPE_WITH_CODE (SoupContentSnifferStream, soup_content_sniffer_stream, G_TYPE_FILTER_INPUT_STREAM,
22 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
23 soup_content_sniffer_stream_pollable_init))
32 struct _SoupContentSnifferStreamPrivate {
33 SoupContentSniffer *sniffer;
37 gsize buffer_size, buffer_nread;
42 GHashTable *sniffed_params;
46 soup_content_sniffer_stream_finalize (GObject *object)
48 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object);
50 if (sniffer->priv->sniffer)
51 g_object_unref (sniffer->priv->sniffer);
52 if (sniffer->priv->msg)
53 g_object_unref (sniffer->priv->msg);
54 if (sniffer->priv->buffer)
55 g_free (sniffer->priv->buffer);
56 if (sniffer->priv->error)
57 g_error_free (sniffer->priv->error);
58 if (sniffer->priv->sniffed_type)
59 g_free (sniffer->priv->sniffed_type);
60 if (sniffer->priv->sniffed_params)
61 g_hash_table_unref (sniffer->priv->sniffed_params);
63 G_OBJECT_CLASS (soup_content_sniffer_stream_parent_class)->finalize (object);
67 soup_content_sniffer_stream_set_property (GObject *object, guint prop_id,
68 const GValue *value, GParamSpec *pspec)
70 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object);
74 sniffer->priv->sniffer = g_value_dup_object (value);
75 /* FIXME: supposed to wait until after got-headers for this */
76 sniffer->priv->buffer_size = soup_content_sniffer_get_buffer_size (sniffer->priv->sniffer);
77 sniffer->priv->buffer = g_malloc (sniffer->priv->buffer_size);
80 sniffer->priv->msg = g_value_dup_object (value);
83 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
89 soup_content_sniffer_stream_get_property (GObject *object, guint prop_id,
90 GValue *value, GParamSpec *pspec)
92 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object);
96 g_value_set_object (value, sniffer->priv->sniffer);
99 g_value_set_object (value, sniffer->priv->msg);
102 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
108 read_and_sniff (GInputStream *stream, gboolean blocking,
109 GCancellable *cancellable, GError **error)
111 SoupContentSnifferStreamPrivate *priv = SOUP_CONTENT_SNIFFER_STREAM (stream)->priv;
113 GError *my_error = NULL;
117 nread = g_pollable_stream_read (G_FILTER_INPUT_STREAM (stream)->base_stream,
118 priv->buffer + priv->buffer_nread,
119 priv->buffer_size - priv->buffer_nread,
120 blocking, cancellable, &my_error);
123 priv->buffer_nread += nread;
124 } while (priv->buffer_nread < priv->buffer_size);
126 /* If we got EAGAIN or cancellation before filling the buffer,
127 * just return that right away. Likewise if we got any other
128 * error without ever reading any data. Otherwise, save the
129 * error to return after we're done sniffing.
132 if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
133 g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
134 priv->buffer_nread == 0) {
135 g_propagate_error (error, my_error);
138 priv->error = my_error;
141 /* Sniff, then return the data */
142 buf = soup_buffer_new (SOUP_MEMORY_TEMPORARY, priv->buffer, priv->buffer_nread);
144 soup_content_sniffer_sniff (priv->sniffer, priv->msg, buf,
145 &priv->sniffed_params);
146 soup_buffer_free (buf);
147 priv->sniffing = FALSE;
149 return priv->buffer_nread;
153 read_internal (GInputStream *stream,
157 GCancellable *cancellable,
160 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
163 if (sniffer->priv->error) {
164 g_propagate_error (error, sniffer->priv->error);
165 sniffer->priv->error = NULL;
169 if (sniffer->priv->sniffing) {
170 nread = read_and_sniff (stream, blocking, cancellable, error);
175 if (sniffer->priv->buffer) {
176 nread = MIN (count, sniffer->priv->buffer_nread);
177 memcpy (buffer, sniffer->priv->buffer, nread);
178 if (nread == sniffer->priv->buffer_nread) {
179 g_free (sniffer->priv->buffer);
180 sniffer->priv->buffer = NULL;
182 /* FIXME, inefficient */
183 memmove (sniffer->priv->buffer,
184 sniffer->priv->buffer + nread,
185 sniffer->priv->buffer_nread - nread);
186 sniffer->priv->buffer_nread -= nread;
189 nread = g_pollable_stream_read (G_FILTER_INPUT_STREAM (stream)->base_stream,
190 buffer, count, blocking,
197 soup_content_sniffer_stream_read (GInputStream *stream,
200 GCancellable *cancellable,
203 return read_internal (stream, buffer, count, TRUE,
208 soup_content_sniffer_stream_skip (GInputStream *stream,
210 GCancellable *cancellable,
213 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
216 if (sniffer->priv->sniffing) {
217 /* Read into the internal buffer... */
218 nskipped = soup_content_sniffer_stream_read (stream, NULL, 0, cancellable, error);
221 /* Now fall through */
224 if (sniffer->priv->buffer) {
225 nskipped = MIN (count, sniffer->priv->buffer_nread);
226 if (nskipped == sniffer->priv->buffer_nread) {
227 g_free (sniffer->priv->buffer);
228 sniffer->priv->buffer = NULL;
231 memmove (sniffer->priv->buffer,
232 sniffer->priv->buffer + nskipped,
233 sniffer->priv->buffer_nread - nskipped);
234 sniffer->priv->buffer_nread -= nskipped;
237 nskipped = G_INPUT_STREAM_CLASS (soup_content_sniffer_stream_parent_class)->
238 skip (stream, count, cancellable, error);
244 soup_content_sniffer_stream_is_readable (GPollableInputStream *stream)
246 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
248 if (sniffer->priv->error ||
249 (!sniffer->priv->sniffing && sniffer->priv->buffer))
252 return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (stream)->base_stream));
256 soup_content_sniffer_stream_read_nonblocking (GPollableInputStream *stream,
261 return read_internal (G_INPUT_STREAM (stream), buffer, count,
266 soup_content_sniffer_stream_create_source (GPollableInputStream *stream,
267 GCancellable *cancellable)
269 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
270 GSource *base_source, *pollable_source;
272 if (sniffer->priv->error ||
273 (!sniffer->priv->sniffing && sniffer->priv->buffer))
274 base_source = g_timeout_source_new (0);
276 base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (stream)->base_stream), cancellable);
278 g_source_set_dummy_callback (base_source);
279 pollable_source = g_pollable_source_new (G_OBJECT (stream));
280 g_source_add_child_source (pollable_source, base_source);
281 g_source_unref (base_source);
283 return pollable_source;
287 soup_content_sniffer_stream_init (SoupContentSnifferStream *sniffer)
289 sniffer->priv = G_TYPE_INSTANCE_GET_PRIVATE (sniffer,
290 SOUP_TYPE_CONTENT_SNIFFER_STREAM,
291 SoupContentSnifferStreamPrivate);
292 sniffer->priv->sniffing = TRUE;
296 soup_content_sniffer_stream_class_init (SoupContentSnifferStreamClass *sniffer_class)
298 GObjectClass *object_class = G_OBJECT_CLASS (sniffer_class);
299 GInputStreamClass *input_stream_class =
300 G_INPUT_STREAM_CLASS (sniffer_class);
302 g_type_class_add_private (sniffer_class, sizeof (SoupContentSnifferStreamPrivate));
304 object_class->finalize = soup_content_sniffer_stream_finalize;
305 object_class->set_property = soup_content_sniffer_stream_set_property;
306 object_class->get_property = soup_content_sniffer_stream_get_property;
308 input_stream_class->read_fn = soup_content_sniffer_stream_read;
309 input_stream_class->skip = soup_content_sniffer_stream_skip;
311 g_object_class_install_property (
312 object_class, PROP_SNIFFER,
313 g_param_spec_object ("sniffer",
315 "The stream's SoupContentSniffer",
316 SOUP_TYPE_CONTENT_SNIFFER,
317 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
318 g_object_class_install_property (
319 object_class, PROP_MESSAGE,
320 g_param_spec_object ("message",
322 "The stream's SoupMessage",
324 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
328 soup_content_sniffer_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
329 gpointer interface_data)
331 pollable_interface->is_readable = soup_content_sniffer_stream_is_readable;
332 pollable_interface->read_nonblocking = soup_content_sniffer_stream_read_nonblocking;
333 pollable_interface->create_source = soup_content_sniffer_stream_create_source;
337 soup_content_sniffer_stream_new (SoupContentSniffer *sniffer,
339 GInputStream *base_stream)
341 return g_object_new (SOUP_TYPE_CONTENT_SNIFFER_STREAM,
342 "base-stream", base_stream,
349 soup_content_sniffer_stream_is_ready (SoupContentSnifferStream *sniffer,
351 GCancellable *cancellable,
354 if (!sniffer->priv->sniffing)
357 return read_and_sniff (G_INPUT_STREAM (sniffer), blocking,
358 cancellable, error) != -1;
362 soup_content_sniffer_stream_sniff (SoupContentSnifferStream *sniffer,
366 *params = sniffer->priv->sniffed_params;
367 return sniffer->priv->sniffed_type;