2010-10-13 Sergio Villar Senin <svillar@igalia.com>
[WebKit-https.git] / WebCore / platform / network / soup / cache / soup-http-input-stream.c
1 /* soup-input-stream.c, based on gsocketinputstream.c
2  *
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  * Copyright (C) 2010 Igalia, S.L.
5  *
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.
10  *
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.
15  *
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.
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25
26 #include <glib.h>
27 #include <gio/gio.h>
28
29 #include <libsoup/soup.h>
30
31 #include "soup-http-input-stream.h"
32
33 static void webkit_soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface);
34
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))
38
39 typedef void (*WebKitSoupHTTPInputStreamCallback)(GInputStream *);
40
41 typedef struct {
42         SoupSession *session;
43         GMainContext *async_context;
44         SoupMessage *msg;
45         gboolean got_headers, finished;
46         goffset offset;
47
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;
54
55         guchar *leftover_buffer;
56         gsize leftover_bufsize, leftover_offset;
57
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))
64
65
66 static gssize   webkit_soup_http_input_stream_read (GInputStream         *stream,
67                                                     void                 *buffer,
68                                                     gsize count,
69                                                     GCancellable         *cancellable,
70                                                     GError              **error);
71 static gboolean webkit_soup_http_input_stream_close (GInputStream         *stream,
72                                                      GCancellable         *cancellable,
73                                                      GError              **error);
74 static void     webkit_soup_http_input_stream_read_async (GInputStream         *stream,
75                                                           void                 *buffer,
76                                                           gsize count,
77                                                           int io_priority,
78                                                           GCancellable         *cancellable,
79                                                           GAsyncReadyCallback callback,
80                                                           gpointer data);
81 static gssize   webkit_soup_http_input_stream_read_finish (GInputStream         *stream,
82                                                            GAsyncResult         *result,
83                                                            GError              **error);
84 static void     webkit_soup_http_input_stream_close_async (GInputStream         *stream,
85                                                            int io_priority,
86                                                            GCancellable         *cancellable,
87                                                            GAsyncReadyCallback callback,
88                                                            gpointer data);
89 static gboolean webkit_soup_http_input_stream_close_finish (GInputStream         *stream,
90                                                             GAsyncResult         *result,
91                                                             GError              **error);
92
93 static goffset  webkit_soup_http_input_stream_tell (GSeekable            *seekable);
94
95 static gboolean webkit_soup_http_input_stream_can_seek (GSeekable            *seekable);
96 static gboolean webkit_soup_http_input_stream_seek (GSeekable            *seekable,
97                                                     goffset offset,
98                                                     GSeekType type,
99                                                     GCancellable         *cancellable,
100                                                     GError              **error);
101
102 static gboolean webkit_soup_http_input_stream_can_truncate (GSeekable            *seekable);
103 static gboolean webkit_soup_http_input_stream_truncate (GSeekable            *seekable,
104                                                         goffset offset,
105                                                         GCancellable         *cancellable,
106                                                         GError              **error);
107
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);
111
112 static void
113 webkit_soup_http_input_stream_finalize (GObject *object)
114 {
115         WebKitSoupHTTPInputStream *stream = WEBKIT_SOUP_HTTP_INPUT_STREAM (object);
116         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
117
118         g_object_unref (priv->session);
119
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);
125
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);
128 }
129
130 static void
131 webkit_soup_http_input_stream_class_init (WebKitSoupHTTPInputStreamClass *klass)
132 {
133         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
134         GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
135
136         g_type_class_add_private (klass, sizeof (WebKitSoupHTTPInputStreamPrivate));
137
138         gobject_class->finalize = webkit_soup_http_input_stream_finalize;
139
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;
146 }
147
148 static void
149 webkit_soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface)
150 {
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;
156 }
157
158 static void
159 webkit_soup_http_input_stream_init (WebKitSoupHTTPInputStream *stream)
160 {
161         ;
162 }
163
164 static void
165 webkit_soup_http_input_stream_queue_message (WebKitSoupHTTPInputStream *stream)
166 {
167         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
168
169         priv->got_headers = priv->finished = FALSE;
170
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);
174 }
175
176 /**
177  * webkit_soup_http_input_stream_new:
178  * @session: the #SoupSession to use
179  * @msg: the #SoupMessage whose response will be streamed
180  *
181  * Prepares to send @msg over @session, and returns a #GInputStream
182  * that can be used to read the response.
183  *
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.
188  *
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.
191  *
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.
199  *
200  * Returns: a new #GInputStream.
201  **/
202 WebKitSoupHTTPInputStream *
203 webkit_soup_http_input_stream_new (SoupSession *session, SoupMessage *msg)
204 {
205         WebKitSoupHTTPInputStream *stream;
206         WebKitSoupHTTPInputStreamPrivate *priv;
207
208         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
209
210         stream = g_object_new (WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, NULL);
211         priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
212
213         priv->session = g_object_ref (session);
214         priv->async_context = soup_session_get_async_context (session);
215         priv->msg = g_object_ref (msg);
216
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);
223
224         webkit_soup_http_input_stream_queue_message (stream);
225         return stream;
226 }
227
228 static void
229 webkit_soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream)
230 {
231         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
232
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).
237          */
238         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
239                 return;
240
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);
245         }
246
247         if (priv->got_headers_cb)
248                 priv->got_headers_cb (stream);
249 }
250
251 static void
252 webkit_soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk_buffer,
253                                          gpointer stream)
254 {
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;
258
259         /* We only pay attention to the chunk if it's part of a successful
260          * response.
261          */
262         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
263                 return;
264
265         /* Sanity check */
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");
268
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);
272
273                 memcpy (priv->caller_buffer + priv->caller_nread, chunk, nread);
274                 priv->caller_nread += nread;
275                 priv->offset += nread;
276                 chunk += nread;
277                 chunk_size -= nread;
278         }
279
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.
284                  */
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,
289                                 chunk, chunk_size);
290                         priv->leftover_bufsize += chunk_size;
291                 } else {
292                         priv->leftover_bufsize = chunk_size;
293                         priv->leftover_buffer = g_memdup (chunk, chunk_size);
294                         priv->leftover_offset = 0;
295                 }
296         }
297
298         soup_session_pause_message (priv->session, msg);
299         if (priv->got_chunk_cb)
300                 priv->got_chunk_cb (stream);
301 }
302
303 static void
304 webkit_soup_http_input_stream_finished (SoupMessage *msg, gpointer stream)
305 {
306         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
307
308         priv->finished = TRUE;
309
310         if (priv->finished_cb)
311                 priv->finished_cb (stream);
312 }
313
314 static gboolean
315 webkit_soup_http_input_stream_cancelled (GIOChannel *chan, GIOCondition condition,
316                                          gpointer stream)
317 {
318         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
319
320         priv->cancel_watch = NULL;
321
322         soup_session_pause_message (priv->session, priv->msg);
323         if (priv->cancelled_cb)
324                 priv->cancelled_cb (stream);
325
326         return FALSE;
327 }
328
329 static void
330 webkit_soup_http_input_stream_prepare_for_io (GInputStream *stream,
331                                               GCancellable *cancellable,
332                                               guchar       *buffer,
333                                               gsize count)
334 {
335         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
336         int cancel_fd;
337
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,
345                                                         stream);
346                 g_io_channel_unref (chan);
347         }
348
349         priv->caller_buffer = buffer;
350         priv->caller_bufsize = count;
351         priv->caller_nread = 0;
352
353         if (priv->got_headers)
354                 soup_session_unpause_message (priv->session, priv->msg);
355 }
356
357 static void
358 webkit_soup_http_input_stream_done_io (GInputStream *stream)
359 {
360         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
361
362         if (priv->cancel_watch) {
363                 g_source_destroy (priv->cancel_watch);
364                 priv->cancel_watch = NULL;
365                 g_cancellable_release_fd (priv->cancellable);
366         }
367         priv->cancellable = NULL;
368
369         priv->caller_buffer = NULL;
370         priv->caller_bufsize = 0;
371 }
372
373 static gboolean
374 set_error_if_http_failed (SoupMessage *msg, GError **error)
375 {
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);
379                 return TRUE;
380         }
381         return FALSE;
382 }
383
384 static gsize
385 read_from_leftover (WebKitSoupHTTPInputStreamPrivate *priv,
386                     gpointer buffer, gsize bufsize)
387 {
388         gsize nread;
389
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);
393
394                 g_free (priv->leftover_buffer);
395                 priv->leftover_buffer = NULL;
396                 priv->leftover_bufsize = priv->leftover_offset = 0;
397         } else {
398                 nread = bufsize;
399                 memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread);
400                 priv->leftover_offset += nread;
401         }
402
403         priv->offset += nread;
404         return nread;
405 }
406
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.
410  */
411 static gboolean
412 webkit_soup_http_input_stream_send_internal (GInputStream  *stream,
413                                              GCancellable  *cancellable,
414                                              GError       **error)
415 {
416         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
417
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);
423
424         if (g_cancellable_set_error_if_cancelled (cancellable, error))
425                 return FALSE;
426         else if (set_error_if_http_failed (priv->msg, error))
427                 return FALSE;
428         return TRUE;
429 }
430
431 static void
432 send_sync_finished (GInputStream *stream)
433 {
434         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
435         GError *error = NULL;
436
437         if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error))
438                 set_error_if_http_failed (priv->msg, &error);
439
440         priv->got_headers_cb = NULL;
441         priv->finished_cb = NULL;
442
443         /* Wake up the main context iteration */
444         g_source_attach (g_idle_source_new (), NULL);
445 }
446
447 /**
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
452  *
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.
457  *
458  * Return value: %TRUE if msg has a successful (2xx) status, %FALSE if
459  * not.
460  **/
461 gboolean
462 webkit_soup_http_input_stream_send (WebKitSoupHTTPInputStream  *httpstream,
463                                     GCancellable         *cancellable,
464                                     GError              **error)
465 {
466         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream);
467         GInputStream *istream = (GInputStream *)httpstream;
468         gboolean result;
469
470         g_return_val_if_fail (WEBKIT_IS_SOUP_HTTP_INPUT_STREAM (httpstream), FALSE);
471
472         if (!g_input_stream_set_pending (istream, error))
473                 return FALSE;
474
475         priv->got_headers_cb = send_sync_finished;
476         priv->finished_cb = send_sync_finished;
477
478         result = webkit_soup_http_input_stream_send_internal (istream, cancellable, error);
479         g_input_stream_clear_pending (istream);
480
481         return result;
482 }
483
484 static gssize
485 webkit_soup_http_input_stream_read (GInputStream *stream,
486                                     void         *buffer,
487                                     gsize count,
488                                     GCancellable *cancellable,
489                                     GError      **error)
490 {
491         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
492
493         if (priv->finished)
494                 return 0;
495
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);
499
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);
506
507         if (priv->caller_nread > 0)
508                 return priv->caller_nread;
509
510         if (g_cancellable_set_error_if_cancelled (cancellable, error))
511                 return -1;
512         else if (set_error_if_http_failed (priv->msg, error))
513                 return -1;
514         else
515                 return 0;
516 }
517
518 static gboolean
519 webkit_soup_http_input_stream_close (GInputStream *stream,
520                                      GCancellable *cancellable,
521                                      GError      **error)
522 {
523         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
524
525         if (!priv->finished)
526                 soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED);
527
528         return TRUE;
529 }
530
531 static void
532 wrapper_callback (GObject *source_object, GAsyncResult *res,
533                   gpointer user_data)
534 {
535         GInputStream *stream = G_INPUT_STREAM (source_object);
536         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
537
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);
543 }
544
545 static void
546 send_async_thread (GSimpleAsyncResult *res,
547                    GObject *object,
548                    GCancellable *cancellable)
549 {
550         GError *error = NULL;
551         gboolean success;
552
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);
556         if (error) {
557                 g_simple_async_result_set_from_error (res, error);
558                 g_error_free (error);
559         }
560 }
561
562 static void
563 webkit_soup_http_input_stream_send_async_in_thread (GInputStream        *stream,
564                                                     int io_priority,
565                                                     GCancellable        *cancellable,
566                                                     GAsyncReadyCallback callback,
567                                                     gpointer user_data)
568 {
569         GSimpleAsyncResult *res;
570
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);
576 }
577
578 static void
579 send_async_finished (GInputStream *stream)
580 {
581         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
582         GSimpleAsyncResult *result;
583         GError *error = NULL;
584
585         if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error))
586                 set_error_if_http_failed (priv->msg, &error);
587
588         priv->got_headers_cb = NULL;
589         priv->finished_cb = NULL;
590         webkit_soup_http_input_stream_done_io (stream);
591
592         result = priv->result;
593         priv->result = NULL;
594
595         g_simple_async_result_set_op_res_gboolean (result, error == NULL);
596         if (error) {
597                 g_simple_async_result_set_from_error (result, error);
598                 g_error_free (error);
599         }
600         g_simple_async_result_complete (result);
601 }
602
603 static void
604 webkit_soup_http_input_stream_send_async_internal (GInputStream        *stream,
605                                                    int io_priority,
606                                                    GCancellable        *cancellable,
607                                                    GAsyncReadyCallback callback,
608                                                    gpointer user_data)
609 {
610         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
611
612         g_object_ref (stream);
613         priv->outstanding_callback = callback;
614
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.
618          */
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);
622                 return;
623         }
624
625         priv->got_headers_cb = send_async_finished;
626         priv->finished_cb = send_async_finished;
627
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);
632 }
633
634 /**
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
641  *
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.
646  **/
647 void
648 webkit_soup_http_input_stream_send_async (WebKitSoupHTTPInputStream *httpstream,
649                                           int io_priority,
650                                           GCancellable        *cancellable,
651                                           GAsyncReadyCallback callback,
652                                           gpointer user_data)
653 {
654         GInputStream *istream = (GInputStream *)httpstream;
655         GError *error = NULL;
656
657         g_return_if_fail (WEBKIT_IS_SOUP_HTTP_INPUT_STREAM (httpstream));
658
659         if (!g_input_stream_set_pending (istream, &error)) {
660                 g_simple_async_report_gerror_in_idle (G_OBJECT (httpstream),
661                                                       callback,
662                                                       user_data,
663                                                       error);
664                 g_error_free (error);
665                 return;
666         }
667         webkit_soup_http_input_stream_send_async_internal (istream, io_priority, cancellable,
668                                                     callback, user_data);
669 }
670
671 /**
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
676  * ignore.
677  *
678  * Finishes a webkit_soup_http_input_stream_send_async() operation.
679  *
680  * Return value: %TRUE if the message was sent successfully and
681  * received a successful status code, %FALSE if not.
682  **/
683 gboolean
684 webkit_soup_http_input_stream_send_finish (WebKitSoupHTTPInputStream  *httpstream,
685                                            GAsyncResult         *result,
686                                            GError              **error)
687 {
688         GSimpleAsyncResult *simple;
689
690         g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
691         simple = G_SIMPLE_ASYNC_RESULT (result);
692
693         g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == webkit_soup_http_input_stream_send_async, FALSE);
694
695         if (g_simple_async_result_propagate_error (simple, error))
696                 return FALSE;
697
698         return g_simple_async_result_get_op_res_gboolean (simple);
699 }
700
701 static void
702 read_async_done (GInputStream *stream)
703 {
704         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
705         GSimpleAsyncResult *result;
706         GError *error = NULL;
707
708         result = priv->result;
709         priv->result = NULL;
710
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);
715         } else
716                 g_simple_async_result_set_op_res_gssize (result, priv->caller_nread);
717
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);
722
723         g_simple_async_result_complete (result);
724         g_object_unref (result);
725 }
726
727 static void
728 webkit_soup_http_input_stream_read_async (GInputStream        *stream,
729                                           void                *buffer,
730                                           gsize count,
731                                           int io_priority,
732                                           GCancellable        *cancellable,
733                                           GAsyncReadyCallback callback,
734                                           gpointer user_data)
735 {
736         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
737         GSimpleAsyncResult *result;
738
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.
742          */
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);
747                 return;
748         }
749
750         result = g_simple_async_result_new (G_OBJECT (stream),
751                                             callback, user_data,
752                                             webkit_soup_http_input_stream_read_async);
753
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);
758                 return;
759         }
760
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);
766                 return;
767         }
768
769         priv->result = result;
770
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);
775 }
776
777 static gssize
778 webkit_soup_http_input_stream_read_finish (GInputStream  *stream,
779                                            GAsyncResult  *result,
780                                            GError       **error)
781 {
782         GSimpleAsyncResult *simple;
783
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);
787
788         return g_simple_async_result_get_op_res_gssize (simple);
789 }
790
791 static void
792 webkit_soup_http_input_stream_close_async (GInputStream       *stream,
793                                            int io_priority,
794                                            GCancellable       *cancellable,
795                                            GAsyncReadyCallback callback,
796                                            gpointer user_data)
797 {
798         GSimpleAsyncResult *result;
799         gboolean success;
800         GError *error = NULL;
801
802         result = g_simple_async_result_new (G_OBJECT (stream),
803                                             callback, user_data,
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);
807         if (error) {
808                 g_simple_async_result_set_from_error (result, error);
809                 g_error_free (error);
810         }
811
812         g_simple_async_result_complete_in_idle (result);
813         g_object_unref (result);
814 }
815
816 static gboolean
817 webkit_soup_http_input_stream_close_finish (GInputStream  *stream,
818                                             GAsyncResult  *result,
819                                             GError       **error)
820 {
821         /* Failures handled in generic close_finish code */
822         return TRUE;
823 }
824
825 static goffset
826 webkit_soup_http_input_stream_tell (GSeekable *seekable)
827 {
828         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable);
829
830         return priv->offset;
831 }
832
833 static gboolean
834 webkit_soup_http_input_stream_can_seek (GSeekable *seekable)
835 {
836         return TRUE;
837 }
838
839 extern void soup_message_io_cleanup (SoupMessage *msg);
840
841 static gboolean
842 webkit_soup_http_input_stream_seek (GSeekable     *seekable,
843                                     goffset offset,
844                                     GSeekType type,
845                                     GCancellable  *cancellable,
846                                     GError       **error)
847 {
848         GInputStream *stream = G_INPUT_STREAM (seekable);
849         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable);
850         char *range;
851
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...
857                  */
858
859                 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
860                                      "G_SEEK_END not currently supported");
861                 return FALSE;
862         }
863
864         if (!g_input_stream_set_pending (stream, error))
865                 return FALSE;
866
867         soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED);
868         soup_message_io_cleanup (priv->msg);
869
870         switch (type) {
871                 case G_SEEK_CUR:
872                         offset += priv->offset;
873                 /* fall through */
874
875                 case G_SEEK_SET:
876                         range = g_strdup_printf ("bytes=%" G_GUINT64_FORMAT "-", (guint64)offset);
877                         priv->offset = offset;
878                         break;
879
880                 case G_SEEK_END:
881                         range = NULL; /* keep compilers happy */
882                         g_return_val_if_reached (FALSE);
883                         break;
884
885                 default:
886                         g_return_val_if_reached (FALSE);
887         }
888
889         soup_message_headers_remove (priv->msg->request_headers, "Range");
890         soup_message_headers_append (priv->msg->request_headers, "Range", range);
891         g_free (range);
892
893         webkit_soup_http_input_stream_queue_message (WEBKIT_SOUP_HTTP_INPUT_STREAM (stream));
894
895         g_input_stream_clear_pending (stream);
896         return TRUE;
897 }
898
899 static gboolean
900 webkit_soup_http_input_stream_can_truncate (GSeekable *seekable)
901 {
902         return FALSE;
903 }
904
905 static gboolean
906 webkit_soup_http_input_stream_truncate (GSeekable     *seekable,
907                                         goffset offset,
908                                         GCancellable  *cancellable,
909                                         GError       **error)
910 {
911         g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
912                              "Truncate not allowed on input stream");
913         return FALSE;
914 }
915
916 SoupMessage *
917 webkit_soup_http_input_stream_get_message (WebKitSoupHTTPInputStream *httpstream)
918 {
919         WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream);
920         return priv->msg ? g_object_ref (priv->msg) : NULL;
921 }