1 /* soup-input-stream.c, based on gsocketinputstream.c
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 * Copyright (C) 2010 Igalia, S.L.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19 * Boston, MA 02111-1307, USA.
29 #include <libsoup/soup.h>
31 #include "soup-http-input-stream.h"
33 static void webkit_soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface);
35 G_DEFINE_TYPE_WITH_CODE (WebKitSoupHTTPInputStream, webkit_soup_http_input_stream, G_TYPE_INPUT_STREAM,
36 G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
37 webkit_soup_http_input_stream_seekable_iface_init))
39 typedef void (*WebKitSoupHTTPInputStreamCallback)(GInputStream *);
43 GMainContext *async_context;
45 gboolean got_headers, finished;
48 GCancellable *cancellable;
49 GSource *cancel_watch;
50 WebKitSoupHTTPInputStreamCallback got_headers_cb;
51 WebKitSoupHTTPInputStreamCallback got_chunk_cb;
52 WebKitSoupHTTPInputStreamCallback finished_cb;
53 WebKitSoupHTTPInputStreamCallback cancelled_cb;
55 guchar *leftover_buffer;
56 gsize leftover_bufsize, leftover_offset;
58 guchar *caller_buffer;
59 gsize caller_bufsize, caller_nread;
60 GAsyncReadyCallback outstanding_callback;
61 GSimpleAsyncResult *result;
62 } WebKitSoupHTTPInputStreamPrivate;
63 #define WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStreamPrivate))
66 static gssize webkit_soup_http_input_stream_read (GInputStream *stream,
69 GCancellable *cancellable,
71 static gboolean webkit_soup_http_input_stream_close (GInputStream *stream,
72 GCancellable *cancellable,
74 static void webkit_soup_http_input_stream_read_async (GInputStream *stream,
78 GCancellable *cancellable,
79 GAsyncReadyCallback callback,
81 static gssize webkit_soup_http_input_stream_read_finish (GInputStream *stream,
84 static void webkit_soup_http_input_stream_close_async (GInputStream *stream,
86 GCancellable *cancellable,
87 GAsyncReadyCallback callback,
89 static gboolean webkit_soup_http_input_stream_close_finish (GInputStream *stream,
93 static goffset webkit_soup_http_input_stream_tell (GSeekable *seekable);
95 static gboolean webkit_soup_http_input_stream_can_seek (GSeekable *seekable);
96 static gboolean webkit_soup_http_input_stream_seek (GSeekable *seekable,
99 GCancellable *cancellable,
102 static gboolean webkit_soup_http_input_stream_can_truncate (GSeekable *seekable);
103 static gboolean webkit_soup_http_input_stream_truncate (GSeekable *seekable,
105 GCancellable *cancellable,
108 static void webkit_soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream);
109 static void webkit_soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer stream);
110 static void webkit_soup_http_input_stream_finished (SoupMessage *msg, gpointer stream);
113 webkit_soup_http_input_stream_finalize (GObject *object)
115 WebKitSoupHTTPInputStream *stream = WEBKIT_SOUP_HTTP_INPUT_STREAM (object);
116 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
118 g_object_unref (priv->session);
120 g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_got_headers), stream);
121 g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_got_chunk), stream);
122 g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_finished), stream);
123 g_object_unref (priv->msg);
124 g_free (priv->leftover_buffer);
126 if (G_OBJECT_CLASS (webkit_soup_http_input_stream_parent_class)->finalize)
127 (*G_OBJECT_CLASS (webkit_soup_http_input_stream_parent_class)->finalize)(object);
131 webkit_soup_http_input_stream_class_init (WebKitSoupHTTPInputStreamClass *klass)
133 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
134 GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
136 g_type_class_add_private (klass, sizeof (WebKitSoupHTTPInputStreamPrivate));
138 gobject_class->finalize = webkit_soup_http_input_stream_finalize;
140 stream_class->read_fn = webkit_soup_http_input_stream_read;
141 stream_class->close_fn = webkit_soup_http_input_stream_close;
142 stream_class->read_async = webkit_soup_http_input_stream_read_async;
143 stream_class->read_finish = webkit_soup_http_input_stream_read_finish;
144 stream_class->close_async = webkit_soup_http_input_stream_close_async;
145 stream_class->close_finish = webkit_soup_http_input_stream_close_finish;
149 webkit_soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface)
151 seekable_iface->tell = webkit_soup_http_input_stream_tell;
152 seekable_iface->can_seek = webkit_soup_http_input_stream_can_seek;
153 seekable_iface->seek = webkit_soup_http_input_stream_seek;
154 seekable_iface->can_truncate = webkit_soup_http_input_stream_can_truncate;
155 seekable_iface->truncate_fn = webkit_soup_http_input_stream_truncate;
159 webkit_soup_http_input_stream_init (WebKitSoupHTTPInputStream *stream)
165 webkit_soup_http_input_stream_queue_message (WebKitSoupHTTPInputStream *stream)
167 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
169 priv->got_headers = priv->finished = FALSE;
171 /* Add an extra ref since soup_session_queue_message steals one */
172 g_object_ref (priv->msg);
173 soup_session_queue_message (priv->session, priv->msg, NULL, NULL);
177 * webkit_soup_http_input_stream_new:
178 * @session: the #SoupSession to use
179 * @msg: the #SoupMessage whose response will be streamed
181 * Prepares to send @msg over @session, and returns a #GInputStream
182 * that can be used to read the response.
184 * @msg may not be sent until the first read call; if you need to look
185 * at the status code or response headers before reading the body, you
186 * can use webkit_soup_http_input_stream_send() or webkit_soup_http_input_stream_send_async()
187 * to force the message to be sent and the response headers read.
189 * If @msg gets a non-2xx result, the first read (or send) will return
190 * an error with type %WEBKIT_SOUP_HTTP_INPUT_STREAM_HTTP_ERROR.
192 * Internally, #WebKitSoupHTTPInputStream is implemented using asynchronous I/O,
193 * so if you are using the synchronous API (eg,
194 * g_input_stream_read()), you should create a new #GMainContext and
195 * set it as the %SOUP_SESSION_ASYNC_CONTEXT property on @session. (If
196 * you don't, then synchronous #GInputStream calls will cause the main
197 * loop to be run recursively.) The async #GInputStream API works fine
198 * with %SOUP_SESSION_ASYNC_CONTEXT either set or unset.
200 * Returns: a new #GInputStream.
202 WebKitSoupHTTPInputStream *
203 webkit_soup_http_input_stream_new (SoupSession *session, SoupMessage *msg)
205 WebKitSoupHTTPInputStream *stream;
206 WebKitSoupHTTPInputStreamPrivate *priv;
208 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
210 stream = g_object_new (WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, NULL);
211 priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
213 priv->session = g_object_ref (session);
214 priv->async_context = soup_session_get_async_context (session);
215 priv->msg = g_object_ref (msg);
217 g_signal_connect (msg, "got_headers",
218 G_CALLBACK (webkit_soup_http_input_stream_got_headers), stream);
219 g_signal_connect (msg, "got_chunk",
220 G_CALLBACK (webkit_soup_http_input_stream_got_chunk), stream);
221 g_signal_connect (msg, "finished",
222 G_CALLBACK (webkit_soup_http_input_stream_finished), stream);
224 webkit_soup_http_input_stream_queue_message (stream);
229 webkit_soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream)
231 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
233 /* If the status is unsuccessful, we just ignore the signal and let
234 * libsoup keep going (eventually either it will requeue the request
235 * (after handling authentication/redirection), or else the
236 * "finished" handler will run).
238 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
241 priv->got_headers = TRUE;
242 if (!priv->caller_buffer) {
243 /* Not ready to read the body yet */
244 soup_session_pause_message (priv->session, msg);
247 if (priv->got_headers_cb)
248 priv->got_headers_cb (stream);
252 webkit_soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk_buffer,
255 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
256 const gchar *chunk = chunk_buffer->data;
257 gsize chunk_size = chunk_buffer->length;
259 /* We only pay attention to the chunk if it's part of a successful
262 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
266 if (priv->caller_bufsize == 0 || priv->leftover_bufsize != 0)
267 g_warning ("webkit_soup_http_input_stream_got_chunk called again before previous chunk was processed");
269 /* Copy what we can into priv->caller_buffer */
270 if (priv->caller_bufsize - priv->caller_nread > 0) {
271 gsize nread = MIN (chunk_size, priv->caller_bufsize - priv->caller_nread);
273 memcpy (priv->caller_buffer + priv->caller_nread, chunk, nread);
274 priv->caller_nread += nread;
275 priv->offset += nread;
280 if (chunk_size > 0) {
281 /* Copy the rest into priv->leftover_buffer. If
282 * there's already some data there, realloc and
283 * append. Otherwise just copy.
285 if (priv->leftover_bufsize) {
286 priv->leftover_buffer = g_realloc (priv->leftover_buffer,
287 priv->leftover_bufsize + chunk_size);
288 memcpy (priv->leftover_buffer + priv->leftover_bufsize,
290 priv->leftover_bufsize += chunk_size;
292 priv->leftover_bufsize = chunk_size;
293 priv->leftover_buffer = g_memdup (chunk, chunk_size);
294 priv->leftover_offset = 0;
298 soup_session_pause_message (priv->session, msg);
299 if (priv->got_chunk_cb)
300 priv->got_chunk_cb (stream);
304 webkit_soup_http_input_stream_finished (SoupMessage *msg, gpointer stream)
306 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
308 priv->finished = TRUE;
310 if (priv->finished_cb)
311 priv->finished_cb (stream);
315 webkit_soup_http_input_stream_cancelled (GIOChannel *chan, GIOCondition condition,
318 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
320 priv->cancel_watch = NULL;
322 soup_session_pause_message (priv->session, priv->msg);
323 if (priv->cancelled_cb)
324 priv->cancelled_cb (stream);
330 webkit_soup_http_input_stream_prepare_for_io (GInputStream *stream,
331 GCancellable *cancellable,
335 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
338 priv->cancellable = cancellable;
339 cancel_fd = g_cancellable_get_fd (cancellable);
340 if (cancel_fd != -1) {
341 GIOChannel *chan = g_io_channel_unix_new (cancel_fd);
342 priv->cancel_watch = soup_add_io_watch (priv->async_context, chan,
343 G_IO_IN | G_IO_ERR | G_IO_HUP,
344 webkit_soup_http_input_stream_cancelled,
346 g_io_channel_unref (chan);
349 priv->caller_buffer = buffer;
350 priv->caller_bufsize = count;
351 priv->caller_nread = 0;
353 if (priv->got_headers)
354 soup_session_unpause_message (priv->session, priv->msg);
358 webkit_soup_http_input_stream_done_io (GInputStream *stream)
360 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
362 if (priv->cancel_watch) {
363 g_source_destroy (priv->cancel_watch);
364 priv->cancel_watch = NULL;
365 g_cancellable_release_fd (priv->cancellable);
367 priv->cancellable = NULL;
369 priv->caller_buffer = NULL;
370 priv->caller_bufsize = 0;
374 set_error_if_http_failed (SoupMessage *msg, GError **error)
376 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
377 g_set_error_literal (error, SOUP_HTTP_ERROR,
378 msg->status_code, msg->reason_phrase);
385 read_from_leftover (WebKitSoupHTTPInputStreamPrivate *priv,
386 gpointer buffer, gsize bufsize)
390 if (priv->leftover_bufsize - priv->leftover_offset <= bufsize) {
391 nread = priv->leftover_bufsize - priv->leftover_offset;
392 memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread);
394 g_free (priv->leftover_buffer);
395 priv->leftover_buffer = NULL;
396 priv->leftover_bufsize = priv->leftover_offset = 0;
399 memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread);
400 priv->leftover_offset += nread;
403 priv->offset += nread;
407 /* This does the work of webkit_soup_http_input_stream_send(), assuming that the
408 * GInputStream pending flag has already been set. It is also used by
409 * webkit_soup_http_input_stream_send_async() in some circumstances.
412 webkit_soup_http_input_stream_send_internal (GInputStream *stream,
413 GCancellable *cancellable,
416 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
418 webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, NULL, 0);
419 while (!priv->finished && !priv->got_headers &&
420 !g_cancellable_is_cancelled (cancellable))
421 g_main_context_iteration (priv->async_context, TRUE);
422 webkit_soup_http_input_stream_done_io (stream);
424 if (g_cancellable_set_error_if_cancelled (cancellable, error))
426 else if (set_error_if_http_failed (priv->msg, error))
432 send_sync_finished (GInputStream *stream)
434 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
435 GError *error = NULL;
437 if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error))
438 set_error_if_http_failed (priv->msg, &error);
440 priv->got_headers_cb = NULL;
441 priv->finished_cb = NULL;
443 /* Wake up the main context iteration */
444 g_source_attach (g_idle_source_new (), NULL);
448 * webkit_soup_http_input_stream_send:
449 * @httpstream: a #WebKitSoupHTTPInputStream
450 * @cancellable: optional #GCancellable object, %NULL to ignore.
451 * @error: location to store the error occuring, or %NULL to ignore
453 * Synchronously sends the HTTP request associated with @stream, and
454 * reads the response headers. Call this after webkit_soup_http_input_stream_new()
455 * and before the first g_input_stream_read() if you want to check the
456 * HTTP status code before you start reading.
458 * Return value: %TRUE if msg has a successful (2xx) status, %FALSE if
462 webkit_soup_http_input_stream_send (WebKitSoupHTTPInputStream *httpstream,
463 GCancellable *cancellable,
466 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream);
467 GInputStream *istream = (GInputStream *)httpstream;
470 g_return_val_if_fail (WEBKIT_IS_SOUP_HTTP_INPUT_STREAM (httpstream), FALSE);
472 if (!g_input_stream_set_pending (istream, error))
475 priv->got_headers_cb = send_sync_finished;
476 priv->finished_cb = send_sync_finished;
478 result = webkit_soup_http_input_stream_send_internal (istream, cancellable, error);
479 g_input_stream_clear_pending (istream);
485 webkit_soup_http_input_stream_read (GInputStream *stream,
488 GCancellable *cancellable,
491 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
496 /* If there is data leftover from a previous read, return it. */
497 if (priv->leftover_bufsize)
498 return read_from_leftover (priv, buffer, count);
500 /* No leftover data, accept one chunk from the network */
501 webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, buffer, count);
502 while (!priv->finished && priv->caller_nread == 0 &&
503 !g_cancellable_is_cancelled (cancellable))
504 g_main_context_iteration (priv->async_context, TRUE);
505 webkit_soup_http_input_stream_done_io (stream);
507 if (priv->caller_nread > 0)
508 return priv->caller_nread;
510 if (g_cancellable_set_error_if_cancelled (cancellable, error))
512 else if (set_error_if_http_failed (priv->msg, error))
519 webkit_soup_http_input_stream_close (GInputStream *stream,
520 GCancellable *cancellable,
523 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
526 soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED);
532 wrapper_callback (GObject *source_object, GAsyncResult *res,
535 GInputStream *stream = G_INPUT_STREAM (source_object);
536 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
538 g_input_stream_clear_pending (stream);
539 if (priv->outstanding_callback)
540 (*priv->outstanding_callback)(source_object, res, user_data);
541 priv->outstanding_callback = NULL;
542 g_object_unref (stream);
546 send_async_thread (GSimpleAsyncResult *res,
548 GCancellable *cancellable)
550 GError *error = NULL;
553 success = webkit_soup_http_input_stream_send_internal (G_INPUT_STREAM (object),
554 cancellable, &error);
555 g_simple_async_result_set_op_res_gboolean (res, success);
557 g_simple_async_result_set_from_error (res, error);
558 g_error_free (error);
563 webkit_soup_http_input_stream_send_async_in_thread (GInputStream *stream,
565 GCancellable *cancellable,
566 GAsyncReadyCallback callback,
569 GSimpleAsyncResult *res;
571 res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
572 webkit_soup_http_input_stream_send_async_in_thread);
573 g_simple_async_result_run_in_thread (res, send_async_thread,
574 io_priority, cancellable);
575 g_object_unref (res);
579 send_async_finished (GInputStream *stream)
581 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
582 GSimpleAsyncResult *result;
583 GError *error = NULL;
585 if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error))
586 set_error_if_http_failed (priv->msg, &error);
588 priv->got_headers_cb = NULL;
589 priv->finished_cb = NULL;
590 webkit_soup_http_input_stream_done_io (stream);
592 result = priv->result;
595 g_simple_async_result_set_op_res_gboolean (result, error == NULL);
597 g_simple_async_result_set_from_error (result, error);
598 g_error_free (error);
600 g_simple_async_result_complete (result);
604 webkit_soup_http_input_stream_send_async_internal (GInputStream *stream,
606 GCancellable *cancellable,
607 GAsyncReadyCallback callback,
610 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
612 g_object_ref (stream);
613 priv->outstanding_callback = callback;
615 /* If the session uses the default GMainContext, then we can do
616 * async I/O directly. But if it has its own main context, it's
617 * easier to just run it in another thread.
619 if (soup_session_get_async_context (priv->session)) {
620 webkit_soup_http_input_stream_send_async_in_thread (stream, io_priority, cancellable,
621 wrapper_callback, user_data);
625 priv->got_headers_cb = send_async_finished;
626 priv->finished_cb = send_async_finished;
628 webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, NULL, 0);
629 priv->result = g_simple_async_result_new (G_OBJECT (stream),
630 wrapper_callback, user_data,
631 webkit_soup_http_input_stream_send_async);
635 * webkit_soup_http_input_stream_send_async:
636 * @httpstream: a #WebKitSoupHTTPInputStream
637 * @io_priority: the io priority of the request.
638 * @cancellable: optional #GCancellable object, %NULL to ignore.
639 * @callback: callback to call when the request is satisfied
640 * @user_data: the data to pass to callback function
642 * Asynchronously sends the HTTP request associated with @stream, and
643 * reads the response headers. Call this after webkit_soup_http_input_stream_new()
644 * and before the first g_input_stream_read_async() if you want to
645 * check the HTTP status code before you start reading.
648 webkit_soup_http_input_stream_send_async (WebKitSoupHTTPInputStream *httpstream,
650 GCancellable *cancellable,
651 GAsyncReadyCallback callback,
654 GInputStream *istream = (GInputStream *)httpstream;
655 GError *error = NULL;
657 g_return_if_fail (WEBKIT_IS_SOUP_HTTP_INPUT_STREAM (httpstream));
659 if (!g_input_stream_set_pending (istream, &error)) {
660 g_simple_async_report_gerror_in_idle (G_OBJECT (httpstream),
664 g_error_free (error);
667 webkit_soup_http_input_stream_send_async_internal (istream, io_priority, cancellable,
668 callback, user_data);
672 * webkit_soup_http_input_stream_send_finish:
673 * @httpstream: a #WebKitSoupHTTPInputStream
674 * @result: a #GAsyncResult.
675 * @error: a #GError location to store the error occuring, or %NULL to
678 * Finishes a webkit_soup_http_input_stream_send_async() operation.
680 * Return value: %TRUE if the message was sent successfully and
681 * received a successful status code, %FALSE if not.
684 webkit_soup_http_input_stream_send_finish (WebKitSoupHTTPInputStream *httpstream,
685 GAsyncResult *result,
688 GSimpleAsyncResult *simple;
690 g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
691 simple = G_SIMPLE_ASYNC_RESULT (result);
693 g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == webkit_soup_http_input_stream_send_async, FALSE);
695 if (g_simple_async_result_propagate_error (simple, error))
698 return g_simple_async_result_get_op_res_gboolean (simple);
702 read_async_done (GInputStream *stream)
704 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
705 GSimpleAsyncResult *result;
706 GError *error = NULL;
708 result = priv->result;
711 if (g_cancellable_set_error_if_cancelled (priv->cancellable, &error) ||
712 set_error_if_http_failed (priv->msg, &error)) {
713 g_simple_async_result_set_from_error (result, error);
714 g_error_free (error);
716 g_simple_async_result_set_op_res_gssize (result, priv->caller_nread);
718 priv->got_chunk_cb = NULL;
719 priv->finished_cb = NULL;
720 priv->cancelled_cb = NULL;
721 webkit_soup_http_input_stream_done_io (stream);
723 g_simple_async_result_complete (result);
724 g_object_unref (result);
728 webkit_soup_http_input_stream_read_async (GInputStream *stream,
732 GCancellable *cancellable,
733 GAsyncReadyCallback callback,
736 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
737 GSimpleAsyncResult *result;
739 /* If the session uses the default GMainContext, then we can do
740 * async I/O directly. But if it has its own main context, we fall
741 * back to the async-via-sync-in-another-thread implementation.
743 if (soup_session_get_async_context (priv->session)) {
744 G_INPUT_STREAM_CLASS (webkit_soup_http_input_stream_parent_class)->
745 read_async (stream, buffer, count, io_priority,
746 cancellable, callback, user_data);
750 result = g_simple_async_result_new (G_OBJECT (stream),
752 webkit_soup_http_input_stream_read_async);
754 if (priv->finished) {
755 g_simple_async_result_set_op_res_gssize (result, 0);
756 g_simple_async_result_complete_in_idle (result);
757 g_object_unref (result);
761 if (priv->leftover_bufsize) {
762 gsize nread = read_from_leftover (priv, buffer, count);
763 g_simple_async_result_set_op_res_gssize (result, nread);
764 g_simple_async_result_complete_in_idle (result);
765 g_object_unref (result);
769 priv->result = result;
771 priv->got_chunk_cb = read_async_done;
772 priv->finished_cb = read_async_done;
773 priv->cancelled_cb = read_async_done;
774 webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, buffer, count);
778 webkit_soup_http_input_stream_read_finish (GInputStream *stream,
779 GAsyncResult *result,
782 GSimpleAsyncResult *simple;
784 g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), -1);
785 simple = G_SIMPLE_ASYNC_RESULT (result);
786 g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == webkit_soup_http_input_stream_read_async, -1);
788 return g_simple_async_result_get_op_res_gssize (simple);
792 webkit_soup_http_input_stream_close_async (GInputStream *stream,
794 GCancellable *cancellable,
795 GAsyncReadyCallback callback,
798 GSimpleAsyncResult *result;
800 GError *error = NULL;
802 result = g_simple_async_result_new (G_OBJECT (stream),
804 webkit_soup_http_input_stream_close_async);
805 success = webkit_soup_http_input_stream_close (stream, cancellable, &error);
806 g_simple_async_result_set_op_res_gboolean (result, success);
808 g_simple_async_result_set_from_error (result, error);
809 g_error_free (error);
812 g_simple_async_result_complete_in_idle (result);
813 g_object_unref (result);
817 webkit_soup_http_input_stream_close_finish (GInputStream *stream,
818 GAsyncResult *result,
821 /* Failures handled in generic close_finish code */
826 webkit_soup_http_input_stream_tell (GSeekable *seekable)
828 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable);
834 webkit_soup_http_input_stream_can_seek (GSeekable *seekable)
839 extern void soup_message_io_cleanup (SoupMessage *msg);
842 webkit_soup_http_input_stream_seek (GSeekable *seekable,
845 GCancellable *cancellable,
848 GInputStream *stream = G_INPUT_STREAM (seekable);
849 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable);
852 if (type == G_SEEK_END) {
853 /* FIXME: we could send "bytes=-offset", but unless we
854 * know the Content-Length, we wouldn't be able to
855 * answer a tell() properly. We could find the
856 * Content-Length by doing a HEAD...
859 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
860 "G_SEEK_END not currently supported");
864 if (!g_input_stream_set_pending (stream, error))
867 soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED);
868 soup_message_io_cleanup (priv->msg);
872 offset += priv->offset;
876 range = g_strdup_printf ("bytes=%" G_GUINT64_FORMAT "-", (guint64)offset);
877 priv->offset = offset;
881 range = NULL; /* keep compilers happy */
882 g_return_val_if_reached (FALSE);
886 g_return_val_if_reached (FALSE);
889 soup_message_headers_remove (priv->msg->request_headers, "Range");
890 soup_message_headers_append (priv->msg->request_headers, "Range", range);
893 webkit_soup_http_input_stream_queue_message (WEBKIT_SOUP_HTTP_INPUT_STREAM (stream));
895 g_input_stream_clear_pending (stream);
900 webkit_soup_http_input_stream_can_truncate (GSeekable *seekable)
906 webkit_soup_http_input_stream_truncate (GSeekable *seekable,
908 GCancellable *cancellable,
911 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
912 "Truncate not allowed on input stream");
917 webkit_soup_http_input_stream_get_message (WebKitSoupHTTPInputStream *httpstream)
919 WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream);
920 return priv->msg ? g_object_ref (priv->msg) : NULL;