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.
14 #include "soup-content-sniffer-stream.h"
17 static void soup_content_sniffer_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
19 G_DEFINE_TYPE_WITH_CODE (SoupContentSnifferStream, soup_content_sniffer_stream, G_TYPE_FILTER_INPUT_STREAM,
20 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
21 soup_content_sniffer_stream_pollable_init))
30 struct _SoupContentSnifferStreamPrivate {
31 SoupContentSniffer *sniffer;
35 gsize buffer_size, buffer_nread;
40 GHashTable *sniffed_params;
44 soup_content_sniffer_stream_finalize (GObject *object)
46 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object);
48 g_clear_object (&sniffer->priv->sniffer);
49 g_clear_object (&sniffer->priv->msg);
50 g_free (sniffer->priv->buffer);
51 g_clear_error (&sniffer->priv->error);
52 g_free (sniffer->priv->sniffed_type);
53 g_clear_pointer (&sniffer->priv->sniffed_params, g_hash_table_unref);
55 G_OBJECT_CLASS (soup_content_sniffer_stream_parent_class)->finalize (object);
59 soup_content_sniffer_stream_set_property (GObject *object, guint prop_id,
60 const GValue *value, GParamSpec *pspec)
62 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object);
66 sniffer->priv->sniffer = g_value_dup_object (value);
67 /* FIXME: supposed to wait until after got-headers for this */
68 sniffer->priv->buffer_size = soup_content_sniffer_get_buffer_size (sniffer->priv->sniffer);
69 sniffer->priv->buffer = g_malloc (sniffer->priv->buffer_size);
72 sniffer->priv->msg = g_value_dup_object (value);
75 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
81 soup_content_sniffer_stream_get_property (GObject *object, guint prop_id,
82 GValue *value, GParamSpec *pspec)
84 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object);
88 g_value_set_object (value, sniffer->priv->sniffer);
91 g_value_set_object (value, sniffer->priv->msg);
94 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
100 read_and_sniff (GInputStream *stream, gboolean blocking,
101 GCancellable *cancellable, GError **error)
103 SoupContentSnifferStreamPrivate *priv = SOUP_CONTENT_SNIFFER_STREAM (stream)->priv;
105 GError *my_error = NULL;
109 nread = g_pollable_stream_read (G_FILTER_INPUT_STREAM (stream)->base_stream,
110 priv->buffer + priv->buffer_nread,
111 priv->buffer_size - priv->buffer_nread,
112 blocking, cancellable, &my_error);
115 priv->buffer_nread += nread;
116 } while (priv->buffer_nread < priv->buffer_size);
118 /* If we got EAGAIN or cancellation before filling the buffer,
119 * just return that right away. Likewise if we got any other
120 * error without ever reading any data. Otherwise, save the
121 * error to return after we're done sniffing.
124 if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
125 g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
126 priv->buffer_nread == 0) {
127 g_propagate_error (error, my_error);
130 priv->error = my_error;
133 /* Sniff, then return the data */
134 buf = soup_buffer_new (SOUP_MEMORY_TEMPORARY, priv->buffer, priv->buffer_nread);
136 soup_content_sniffer_sniff (priv->sniffer, priv->msg, buf,
137 &priv->sniffed_params);
138 soup_buffer_free (buf);
139 priv->sniffing = FALSE;
141 return priv->buffer_nread;
145 read_internal (GInputStream *stream,
149 GCancellable *cancellable,
152 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
155 if (sniffer->priv->error) {
156 g_propagate_error (error, sniffer->priv->error);
157 sniffer->priv->error = NULL;
161 if (sniffer->priv->sniffing) {
162 nread = read_and_sniff (stream, blocking, cancellable, error);
167 if (sniffer->priv->buffer) {
168 nread = MIN (count, sniffer->priv->buffer_nread);
169 memcpy (buffer, sniffer->priv->buffer, nread);
170 if (nread == sniffer->priv->buffer_nread) {
171 g_free (sniffer->priv->buffer);
172 sniffer->priv->buffer = NULL;
174 /* FIXME, inefficient */
175 memmove (sniffer->priv->buffer,
176 sniffer->priv->buffer + nread,
177 sniffer->priv->buffer_nread - nread);
178 sniffer->priv->buffer_nread -= nread;
181 nread = g_pollable_stream_read (G_FILTER_INPUT_STREAM (stream)->base_stream,
182 buffer, count, blocking,
189 soup_content_sniffer_stream_read (GInputStream *stream,
192 GCancellable *cancellable,
195 return read_internal (stream, buffer, count, TRUE,
200 soup_content_sniffer_stream_skip (GInputStream *stream,
202 GCancellable *cancellable,
205 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
208 if (sniffer->priv->sniffing) {
209 /* Read into the internal buffer... */
210 nskipped = soup_content_sniffer_stream_read (stream, NULL, 0, cancellable, error);
213 /* Now fall through */
216 if (sniffer->priv->buffer) {
217 nskipped = MIN (count, sniffer->priv->buffer_nread);
218 if (nskipped == sniffer->priv->buffer_nread) {
219 g_free (sniffer->priv->buffer);
220 sniffer->priv->buffer = NULL;
223 memmove (sniffer->priv->buffer,
224 sniffer->priv->buffer + nskipped,
225 sniffer->priv->buffer_nread - nskipped);
226 sniffer->priv->buffer_nread -= nskipped;
229 nskipped = G_INPUT_STREAM_CLASS (soup_content_sniffer_stream_parent_class)->
230 skip (stream, count, cancellable, error);
236 soup_content_sniffer_stream_can_poll (GPollableInputStream *pollable)
238 GInputStream *base_stream = G_FILTER_INPUT_STREAM (pollable)->base_stream;
240 return G_IS_POLLABLE_INPUT_STREAM (base_stream) &&
241 g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream));
246 soup_content_sniffer_stream_is_readable (GPollableInputStream *stream)
248 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
250 if (sniffer->priv->error ||
251 (!sniffer->priv->sniffing && sniffer->priv->buffer))
254 return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (stream)->base_stream));
258 soup_content_sniffer_stream_read_nonblocking (GPollableInputStream *stream,
263 return read_internal (G_INPUT_STREAM (stream), buffer, count,
268 soup_content_sniffer_stream_create_source (GPollableInputStream *stream,
269 GCancellable *cancellable)
271 SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
272 GSource *base_source, *pollable_source;
274 if (sniffer->priv->error ||
275 (!sniffer->priv->sniffing && sniffer->priv->buffer))
276 base_source = g_timeout_source_new (0);
278 base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (stream)->base_stream), cancellable);
280 g_source_set_dummy_callback (base_source);
281 pollable_source = g_pollable_source_new (G_OBJECT (stream));
282 g_source_add_child_source (pollable_source, base_source);
283 g_source_unref (base_source);
285 return pollable_source;
289 soup_content_sniffer_stream_init (SoupContentSnifferStream *sniffer)
291 sniffer->priv = G_TYPE_INSTANCE_GET_PRIVATE (sniffer,
292 SOUP_TYPE_CONTENT_SNIFFER_STREAM,
293 SoupContentSnifferStreamPrivate);
294 sniffer->priv->sniffing = TRUE;
298 soup_content_sniffer_stream_class_init (SoupContentSnifferStreamClass *sniffer_class)
300 GObjectClass *object_class = G_OBJECT_CLASS (sniffer_class);
301 GInputStreamClass *input_stream_class =
302 G_INPUT_STREAM_CLASS (sniffer_class);
304 g_type_class_add_private (sniffer_class, sizeof (SoupContentSnifferStreamPrivate));
306 object_class->finalize = soup_content_sniffer_stream_finalize;
307 object_class->set_property = soup_content_sniffer_stream_set_property;
308 object_class->get_property = soup_content_sniffer_stream_get_property;
310 input_stream_class->read_fn = soup_content_sniffer_stream_read;
311 input_stream_class->skip = soup_content_sniffer_stream_skip;
313 g_object_class_install_property (
314 object_class, PROP_SNIFFER,
315 g_param_spec_object ("sniffer",
317 "The stream's SoupContentSniffer",
318 SOUP_TYPE_CONTENT_SNIFFER,
319 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
320 g_object_class_install_property (
321 object_class, PROP_MESSAGE,
322 g_param_spec_object ("message",
324 "The stream's SoupMessage",
326 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
330 soup_content_sniffer_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
331 gpointer interface_data)
333 pollable_interface->can_poll = soup_content_sniffer_stream_can_poll;
334 pollable_interface->is_readable = soup_content_sniffer_stream_is_readable;
335 pollable_interface->read_nonblocking = soup_content_sniffer_stream_read_nonblocking;
336 pollable_interface->create_source = soup_content_sniffer_stream_create_source;
340 soup_content_sniffer_stream_is_ready (SoupContentSnifferStream *sniffer,
342 GCancellable *cancellable,
345 if (!sniffer->priv->sniffing)
348 return read_and_sniff (G_INPUT_STREAM (sniffer), blocking,
349 cancellable, error) != -1;
353 soup_content_sniffer_stream_sniff (SoupContentSnifferStream *sniffer,
357 *params = sniffer->priv->sniffed_params;
358 return sniffer->priv->sniffed_type;