Git init
[profile/ivi/libsoup2.4.git] / libsoup / soup-message-io.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-message-io.c: HTTP message I/O
4  *
5  * Copyright (C) 2000-2003, Ximian, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include "soup-connection.h"
16 #include "soup-message.h"
17 #include "soup-message-private.h"
18 #include "soup-message-queue.h"
19 #include "soup-misc.h"
20 #include "soup-socket.h"
21 #include "soup-ssl.h"
22
23 typedef enum {
24         SOUP_MESSAGE_IO_CLIENT,
25         SOUP_MESSAGE_IO_SERVER
26 } SoupMessageIOMode;
27
28 typedef enum {
29         SOUP_MESSAGE_IO_STATE_NOT_STARTED,
30         SOUP_MESSAGE_IO_STATE_HEADERS,
31         SOUP_MESSAGE_IO_STATE_BLOCKING,
32         SOUP_MESSAGE_IO_STATE_BODY,
33         SOUP_MESSAGE_IO_STATE_CHUNK_SIZE,
34         SOUP_MESSAGE_IO_STATE_CHUNK,
35         SOUP_MESSAGE_IO_STATE_CHUNK_END,
36         SOUP_MESSAGE_IO_STATE_TRAILERS,
37         SOUP_MESSAGE_IO_STATE_FINISHING,
38         SOUP_MESSAGE_IO_STATE_DONE
39 } SoupMessageIOState;
40
41 #define SOUP_MESSAGE_IO_STATE_ACTIVE(state) \
42         (state != SOUP_MESSAGE_IO_STATE_NOT_STARTED && \
43          state != SOUP_MESSAGE_IO_STATE_BLOCKING && \
44          state != SOUP_MESSAGE_IO_STATE_DONE)
45
46 typedef struct {
47         SoupSocket           *sock;
48         SoupMessageQueueItem *item;
49         SoupMessageIOMode     mode;
50         GCancellable         *cancellable;
51
52         SoupMessageIOState    read_state;
53         SoupEncoding          read_encoding;
54         GByteArray           *read_meta_buf;
55         SoupMessageBody      *read_body;
56         goffset               read_length;
57         gboolean              read_eof_ok;
58
59         gboolean              need_content_sniffed, need_got_chunk;
60         SoupMessageBody      *sniff_data;
61
62         SoupMessageIOState    write_state;
63         SoupEncoding          write_encoding;
64         GString              *write_buf;
65         SoupMessageBody      *write_body;
66         SoupBuffer           *write_chunk;
67         goffset               write_body_offset;
68         goffset               write_length;
69         goffset               written;
70
71         guint read_tag, write_tag, tls_signal_id;
72         GSource *unpause_source;
73
74         SoupMessageGetHeadersFn   get_headers_cb;
75         SoupMessageParseHeadersFn parse_headers_cb;
76         gpointer                  header_data;
77         SoupMessageCompletionFn   completion_cb;
78         gpointer                  completion_data;
79 } SoupMessageIOData;
80         
81
82 /* Put these around callback invocation if there is code afterward
83  * that depends on the IO having not been cancelled.
84  */
85 #define dummy_to_make_emacs_happy {
86 #define SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK { gboolean cancelled; g_object_ref (msg);
87 #define SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED cancelled = (priv->io_data != io); g_object_unref (msg); if (cancelled || (!io->read_tag && !io->write_tag)) return; }
88 #define SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED(val) cancelled = (priv->io_data != io); g_object_unref (msg); if (cancelled || (!io->read_tag && !io->write_tag)) return val; }
89
90 #define RESPONSE_BLOCK_SIZE 8192
91
92 void
93 soup_message_io_cleanup (SoupMessage *msg)
94 {
95         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
96         SoupMessageIOData *io;
97
98         soup_message_io_stop (msg);
99
100         io = priv->io_data;
101         if (!io)
102                 return;
103         priv->io_data = NULL;
104
105         if (io->tls_signal_id)
106                 g_signal_handler_disconnect (io->sock, io->tls_signal_id);
107         if (io->sock)
108                 g_object_unref (io->sock);
109         if (io->item)
110                 soup_message_queue_item_unref (io->item);
111
112         g_byte_array_free (io->read_meta_buf, TRUE);
113
114         g_string_free (io->write_buf, TRUE);
115         if (io->write_chunk)
116                 soup_buffer_free (io->write_chunk);
117
118         if (io->sniff_data)
119                 soup_message_body_free (io->sniff_data);
120
121         g_slice_free (SoupMessageIOData, io);
122 }
123
124 void
125 soup_message_io_stop (SoupMessage *msg)
126 {
127         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
128         SoupMessageIOData *io = priv->io_data;
129
130         if (!io)
131                 return;
132
133         if (io->read_tag) {
134                 g_signal_handler_disconnect (io->sock, io->read_tag);
135                 io->read_tag = 0;
136         }
137         if (io->write_tag) {
138                 g_signal_handler_disconnect (io->sock, io->write_tag);
139                 io->write_tag = 0;
140         }
141
142         if (io->unpause_source) {
143                 g_source_destroy (io->unpause_source);
144                 io->unpause_source = NULL;
145         }
146
147         if (io->read_state < SOUP_MESSAGE_IO_STATE_FINISHING)
148                 soup_socket_disconnect (io->sock);
149         else if (io->item && io->item->conn)
150                 soup_connection_set_state (io->item->conn, SOUP_CONNECTION_IDLE);
151 }
152
153 #define SOUP_MESSAGE_IO_EOL            "\r\n"
154 #define SOUP_MESSAGE_IO_EOL_LEN        2
155
156 void
157 soup_message_io_finished (SoupMessage *msg)
158 {
159         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
160         SoupMessageIOData *io = priv->io_data;
161         SoupMessageCompletionFn completion_cb = io->completion_cb;
162         gpointer completion_data = io->completion_data;
163
164         g_object_ref (msg);
165         soup_message_io_cleanup (msg);
166         if (completion_cb)
167                 completion_cb (msg, completion_data);
168         g_object_unref (msg);
169 }
170
171 static void io_read (SoupSocket *sock, SoupMessage *msg);
172
173 static gboolean
174 request_is_idempotent (SoupMessage *msg)
175 {
176         /* FIXME */
177         return (msg->method == SOUP_METHOD_GET);
178 }
179
180 static void
181 io_error (SoupSocket *sock, SoupMessage *msg, GError *error)
182 {
183         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
184         SoupMessageIOData *io = priv->io_data;
185
186         if (error && error->domain == G_TLS_ERROR) {
187                 soup_message_set_status_full (msg,
188                                               SOUP_STATUS_SSL_FAILED,
189                                               error->message);
190         } else if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
191                    io->read_state <= SOUP_MESSAGE_IO_STATE_HEADERS &&
192                    io->read_meta_buf->len == 0 &&
193                    soup_connection_get_ever_used (io->item->conn) &&
194                    !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT) &&
195                    request_is_idempotent (msg)) {
196                 /* Connection got closed, but we can safely try again */
197                 io->item->state = SOUP_MESSAGE_RESTARTING;
198         } else if (!SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code))
199                 soup_message_set_status (msg, SOUP_STATUS_IO_ERROR);
200
201         if (error)
202                 g_error_free (error);
203
204         soup_message_io_finished (msg);
205 }
206
207 static gboolean
208 io_handle_sniffing (SoupMessage *msg, gboolean done_reading)
209 {
210         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
211         SoupMessageIOData *io = priv->io_data;
212         SoupBuffer *sniffed_buffer;
213         char *sniffed_mime_type;
214         GHashTable *params = NULL;
215
216         if (!priv->sniffer)
217                 return TRUE;
218
219         if (!io->sniff_data) {
220                 io->sniff_data = soup_message_body_new ();
221                 io->need_content_sniffed = TRUE;
222         }
223
224         if (io->need_content_sniffed) {
225                 if (io->sniff_data->length < priv->bytes_for_sniffing &&
226                     !done_reading)
227                         return TRUE;
228
229                 io->need_content_sniffed = FALSE;
230                 sniffed_buffer = soup_message_body_flatten (io->sniff_data);
231                 sniffed_mime_type = soup_content_sniffer_sniff (priv->sniffer, msg, sniffed_buffer, &params);
232
233                 SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
234                 soup_message_content_sniffed (msg, sniffed_mime_type, params);
235                 g_free (sniffed_mime_type);
236                 if (params)
237                         g_hash_table_destroy (params);
238                 if (sniffed_buffer)
239                         soup_buffer_free (sniffed_buffer);
240                 SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
241         }
242
243         if (io->need_got_chunk) {
244                 io->need_got_chunk = FALSE;
245                 sniffed_buffer = soup_message_body_flatten (io->sniff_data);
246
247                 SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
248                 soup_message_got_chunk (msg, sniffed_buffer);
249                 soup_buffer_free (sniffed_buffer);
250                 SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
251         }
252
253         return TRUE;
254 }
255
256 /* Reads data from io->sock into io->read_meta_buf. If @to_blank is
257  * %TRUE, it reads up until a blank line ("CRLF CRLF" or "LF LF").
258  * Otherwise, it reads up until a single CRLF or LF.
259  *
260  * This function is used to read metadata, and read_body_chunk() is
261  * used to read the message body contents.
262  *
263  * read_metadata, read_body_chunk, and write_data all use the same
264  * convention for return values: if they return %TRUE, it means
265  * they've completely finished the requested read/write, and the
266  * caller should move on to the next step. If they return %FALSE, it
267  * means that either (a) the socket returned SOUP_SOCKET_WOULD_BLOCK,
268  * so the caller should give up for now and wait for the socket to
269  * emit a signal, or (b) the socket returned an error, and io_error()
270  * was called to process it and cancel the I/O. So either way, if the
271  * function returns %FALSE, the caller should return immediately.
272  */
273 static gboolean
274 read_metadata (SoupMessage *msg, gboolean to_blank)
275 {
276         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
277         SoupMessageIOData *io = priv->io_data;
278         SoupSocketIOStatus status;
279         guchar read_buf[RESPONSE_BLOCK_SIZE];
280         gsize nread;
281         gboolean got_lf;
282         GError *error = NULL;
283
284         while (1) {
285                 status = soup_socket_read_until (io->sock, read_buf,
286                                                  sizeof (read_buf),
287                                                  "\n", 1, &nread, &got_lf,
288                                                  io->cancellable, &error);
289                 switch (status) {
290                 case SOUP_SOCKET_OK:
291                         g_byte_array_append (io->read_meta_buf, read_buf, nread);
292                         break;
293
294                 case SOUP_SOCKET_EOF:
295                         /* More lame server handling... deal with
296                          * servers that don't send the final chunk.
297                          */
298                         if (io->read_state == SOUP_MESSAGE_IO_STATE_CHUNK_SIZE &&
299                             io->read_meta_buf->len == 0) {
300                                 g_byte_array_append (io->read_meta_buf,
301                                                      (guchar *)"0\r\n", 3);
302                                 got_lf = TRUE;
303                                 break;
304                         } else if (io->read_state == SOUP_MESSAGE_IO_STATE_TRAILERS &&
305                                    io->read_meta_buf->len == 0) {
306                                 g_byte_array_append (io->read_meta_buf,
307                                                      (guchar *)"\r\n", 2);
308                                 got_lf = TRUE;
309                                 break;
310                         }
311                         /* else fall through */
312
313                 case SOUP_SOCKET_ERROR:
314                         io_error (io->sock, msg, error);
315                         return FALSE;
316
317                 case SOUP_SOCKET_WOULD_BLOCK:
318                         return FALSE;
319                 }
320
321                 if (got_lf) {
322                         if (!to_blank)
323                                 break;
324                         if (nread == 1 &&
325                             !strncmp ((char *)io->read_meta_buf->data +
326                                       io->read_meta_buf->len - 2,
327                                       "\n\n", 2))
328                                 break;
329                         else if (nread == 2 &&
330                                  !strncmp ((char *)io->read_meta_buf->data +
331                                            io->read_meta_buf->len - 3,
332                                            "\n\r\n", 3))
333                                 break;
334                 }
335         }
336
337         return TRUE;
338 }
339
340 static SoupBuffer *
341 content_decode_one (SoupBuffer *buf, GConverter *converter, GError **error)
342 {
343         gsize outbuf_length, outbuf_used, outbuf_cur, input_used, input_cur;
344         char *outbuf;
345         GConverterResult result;
346
347         outbuf_length = MAX (buf->length * 2, 1024);
348         outbuf = g_malloc (outbuf_length);
349         outbuf_cur = input_cur = 0;
350
351         do {
352                 result = g_converter_convert (
353                         converter,
354                         buf->data + input_cur, buf->length - input_cur,
355                         outbuf + outbuf_cur, outbuf_length - outbuf_cur,
356                         0, &input_used, &outbuf_used, error);
357                 input_cur += input_used;
358                 outbuf_cur += outbuf_used;
359
360                 if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NO_SPACE) ||
361                     (!*error && outbuf_cur == outbuf_length)) {
362                         g_clear_error (error);
363                         outbuf_length *= 2;
364                         outbuf = g_realloc (outbuf, outbuf_length);
365                 } else if (*error) {
366                         /* GZlibDecompressor can't ever return
367                          * G_IO_ERROR_PARTIAL_INPUT unless we pass it
368                          * input_length = 0, which we don't. Other
369                          * converters might of course, so eventually
370                          * this code needs to be rewritten to deal
371                          * with that.
372                          */
373                         g_free (outbuf);
374                         return NULL;
375                 }
376         } while (input_cur < buf->length && result != G_CONVERTER_FINISHED);
377
378         if (outbuf_cur)
379                 return soup_buffer_new (SOUP_MEMORY_TAKE, outbuf, outbuf_cur);
380         else {
381                 g_free (outbuf);
382                 return NULL;
383         }
384 }
385
386 static SoupBuffer *
387 content_decode (SoupMessage *msg, SoupBuffer *buf)
388 {
389         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
390         GConverter *decoder;
391         SoupBuffer *decoded;
392         GError *error = NULL;
393         GSList *d;
394
395         for (d = priv->decoders; d; d = d->next) {
396                 decoder = d->data;
397
398                 decoded = content_decode_one (buf, decoder, &error);
399                 if (error) {
400                         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED))
401                                 g_warning ("Content-Decoding error: %s\n", error->message);
402                         g_error_free (error);
403
404                         soup_message_set_flags (msg, priv->msg_flags & ~SOUP_MESSAGE_CONTENT_DECODED);
405                         break;
406                 }
407                 if (buf)
408                         soup_buffer_free (buf);
409
410                 if (decoded)
411                         buf = decoded;
412                 else
413                         return NULL;
414         }
415
416         return buf;
417 }
418
419 /* Reads as much message body data as is available on io->sock (but no
420  * further than the end of the current message body or chunk). On a
421  * successful read, emits "got_chunk" (possibly multiple times), and
422  * (unless told not to) appends the chunk to io->read_body.
423  *
424  * See the note at read_metadata() for an explanation of the return
425  * value.
426  */
427 static gboolean
428 read_body_chunk (SoupMessage *msg)
429 {
430         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
431         SoupMessageIOData *io = priv->io_data;
432         SoupSocketIOStatus status;
433         guchar *stack_buf = NULL;
434         gsize len;
435         gboolean read_to_eof = (io->read_encoding == SOUP_ENCODING_EOF);
436         gsize nread;
437         GError *error = NULL;
438         SoupBuffer *buffer;
439
440         if (!io_handle_sniffing (msg, FALSE))
441                 return FALSE;
442
443         while (read_to_eof || io->read_length > 0) {
444                 if (priv->chunk_allocator) {
445                         buffer = priv->chunk_allocator (msg, io->read_length, priv->chunk_allocator_data);
446                         if (!buffer) {
447                                 soup_message_io_pause (msg);
448                                 return FALSE;
449                         }
450                 } else {
451                         if (!stack_buf)
452                                 stack_buf = alloca (RESPONSE_BLOCK_SIZE);
453                         buffer = soup_buffer_new (SOUP_MEMORY_TEMPORARY,
454                                                   stack_buf,
455                                                   RESPONSE_BLOCK_SIZE);
456                 }
457
458                 if (read_to_eof)
459                         len = buffer->length;
460                 else
461                         len = MIN (buffer->length, io->read_length);
462
463                 status = soup_socket_read (io->sock,
464                                            (guchar *)buffer->data, len,
465                                            &nread, io->cancellable, &error);
466
467                 if (status == SOUP_SOCKET_OK && nread) {
468                         buffer->length = nread;
469                         io->read_length -= nread;
470
471                         buffer = content_decode (msg, buffer);
472                         if (!buffer)
473                                 continue;
474
475                         soup_message_body_got_chunk (io->read_body, buffer);
476
477                         if (io->need_content_sniffed) {
478                                 soup_message_body_append_buffer (io->sniff_data, buffer);
479                                 soup_buffer_free (buffer);
480                                 io->need_got_chunk = TRUE;
481                                 if (!io_handle_sniffing (msg, FALSE))
482                                         return FALSE;
483                                 continue;
484                         }
485
486                         SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
487                         soup_message_got_chunk (msg, buffer);
488                         soup_buffer_free (buffer);
489                         SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
490                         continue;
491                 }
492
493                 soup_buffer_free (buffer);
494                 switch (status) {
495                 case SOUP_SOCKET_OK:
496                         break;
497
498                 case SOUP_SOCKET_EOF:
499                         if (io->read_eof_ok) {
500                                 io->read_length = 0;
501                                 return TRUE;
502                         }
503                         /* else fall through */
504
505                 case SOUP_SOCKET_ERROR:
506                         io_error (io->sock, msg, error);
507                         return FALSE;
508
509                 case SOUP_SOCKET_WOULD_BLOCK:
510                         return FALSE;
511                 }
512         }
513
514         return TRUE;
515 }
516
517 /* Attempts to write @len bytes from @data. See the note at
518  * read_metadata() for an explanation of the return value.
519  */
520 static gboolean
521 write_data (SoupMessage *msg, const char *data, guint len, gboolean body)
522 {
523         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
524         SoupMessageIOData *io = priv->io_data;
525         SoupSocketIOStatus status;
526         gsize nwrote;
527         GError *error = NULL;
528         SoupBuffer *chunk;
529         const char *start;
530
531         while (len > io->written) {
532                 status = soup_socket_write (io->sock,
533                                             data + io->written,
534                                             len - io->written,
535                                             &nwrote,
536                                             io->cancellable, &error);
537                 switch (status) {
538                 case SOUP_SOCKET_EOF:
539                 case SOUP_SOCKET_ERROR:
540                         io_error (io->sock, msg, error);
541                         return FALSE;
542
543                 case SOUP_SOCKET_WOULD_BLOCK:
544                         return FALSE;
545
546                 case SOUP_SOCKET_OK:
547                         start = data + io->written;
548                         io->written += nwrote;
549
550                         if (body) {
551                                 if (io->write_length)
552                                         io->write_length -= nwrote;
553
554                                 chunk = soup_buffer_new (SOUP_MEMORY_TEMPORARY,
555                                                          start, nwrote);
556                                 SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
557                                 soup_message_wrote_body_data (msg, chunk);
558                                 soup_buffer_free (chunk);
559                                 SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
560                         }
561                         break;
562                 }
563         }
564
565         io->written = 0;
566         return TRUE;
567 }
568
569 static inline SoupMessageIOState
570 io_body_state (SoupEncoding encoding)
571 {
572         if (encoding == SOUP_ENCODING_CHUNKED)
573                 return SOUP_MESSAGE_IO_STATE_CHUNK_SIZE;
574         else
575                 return SOUP_MESSAGE_IO_STATE_BODY;
576 }
577
578 /*
579  * There are two request/response formats: the basic request/response,
580  * possibly with one or more unsolicited informational responses (such
581  * as the WebDAV "102 Processing" response):
582  *
583  *     Client                            Server
584  *      W:HEADERS  / R:NOT_STARTED    ->  R:HEADERS  / W:NOT_STARTED
585  *      W:BODY     / R:NOT_STARTED    ->  R:BODY     / W:NOT_STARTED
586  *     [W:DONE     / R:HEADERS (1xx)  <-  R:DONE     / W:HEADERS (1xx) ...]
587  *      W:DONE     / R:HEADERS        <-  R:DONE     / W:HEADERS
588  *      W:DONE     / R:BODY           <-  R:DONE     / W:BODY
589  *      W:DONE     / R:DONE               R:DONE     / W:DONE
590  *     
591  * and the "Expect: 100-continue" request/response, with the client
592  * blocking halfway through its request, and then either continuing or
593  * aborting, depending on the server response:
594  *
595  *     Client                            Server
596  *      W:HEADERS  / R:NOT_STARTED    ->  R:HEADERS  / W:NOT_STARTED
597  *      W:BLOCKING / R:HEADERS        <-  R:BLOCKING / W:HEADERS
598  *     [W:BODY     / R:BLOCKING       ->  R:BODY     / W:BLOCKING]
599  *     [W:DONE     / R:HEADERS        <-  R:DONE     / W:HEADERS]
600  *      W:DONE     / R:BODY           <-  R:DONE     / W:BODY
601  *      W:DONE     / R:DONE               R:DONE     / W:DONE
602  */
603
604 static void
605 io_write (SoupSocket *sock, SoupMessage *msg)
606 {
607         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
608         SoupMessageIOData *io = priv->io_data;
609
610  write_more:
611         switch (io->write_state) {
612         case SOUP_MESSAGE_IO_STATE_NOT_STARTED:
613                 return;
614
615
616         case SOUP_MESSAGE_IO_STATE_HEADERS:
617                 if (!io->write_buf->len) {
618                         io->get_headers_cb (msg, io->write_buf,
619                                             &io->write_encoding,
620                                             io->header_data);
621                         if (!io->write_buf->len) {
622                                 soup_message_io_pause (msg);
623                                 return;
624                         }
625                 }
626
627                 if (!write_data (msg, io->write_buf->str,
628                                  io->write_buf->len, FALSE))
629                         return;
630
631                 g_string_truncate (io->write_buf, 0);
632
633                 if (io->write_encoding == SOUP_ENCODING_CONTENT_LENGTH) {
634                         SoupMessageHeaders *hdrs =
635                                 (io->mode == SOUP_MESSAGE_IO_CLIENT) ?
636                                 msg->request_headers : msg->response_headers;
637                         io->write_length = soup_message_headers_get_content_length (hdrs);
638                 }
639
640                 if (io->mode == SOUP_MESSAGE_IO_SERVER &&
641                     SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
642                         if (msg->status_code == SOUP_STATUS_CONTINUE) {
643                                 /* Stop and wait for the body now */
644                                 io->write_state =
645                                         SOUP_MESSAGE_IO_STATE_BLOCKING;
646                                 io->read_state = io_body_state (io->read_encoding);
647                         } else {
648                                 /* We just wrote a 1xx response
649                                  * header, so stay in STATE_HEADERS.
650                                  * (The caller will pause us from the
651                                  * wrote_informational callback if he
652                                  * is not ready to send the final
653                                  * response.)
654                                  */
655                         }
656                 } else if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
657                            soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) {
658                         /* Need to wait for the Continue response */
659                         io->write_state = SOUP_MESSAGE_IO_STATE_BLOCKING;
660                         io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
661                 } else {
662                         io->write_state = io_body_state (io->write_encoding);
663
664                         /* If the client was waiting for a Continue
665                          * but we sent something else, then they're
666                          * now done writing.
667                          */
668                         if (io->mode == SOUP_MESSAGE_IO_SERVER &&
669                             io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING)
670                                 io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
671                 }
672
673                 SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
674                 if (SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
675                         soup_message_wrote_informational (msg);
676                         soup_message_cleanup_response (msg);
677                 } else
678                         soup_message_wrote_headers (msg);
679                 SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
680                 break;
681
682
683         case SOUP_MESSAGE_IO_STATE_BLOCKING:
684                 io_read (sock, msg);
685
686                 /* If io_read reached a point where we could write
687                  * again, it would have recursively called io_write.
688                  * So (a) we don't need to try to keep writing, and
689                  * (b) we can't anyway, because msg may have been
690                  * destroyed.
691                  */
692                 return;
693
694
695         case SOUP_MESSAGE_IO_STATE_BODY:
696                 if (!io->write_length && io->write_encoding != SOUP_ENCODING_EOF) {
697                 wrote_body:
698                         io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
699
700                         SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
701                         soup_message_wrote_body (msg);
702                         SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
703                         break;
704                 }
705
706                 if (!io->write_chunk) {
707                         io->write_chunk = soup_message_body_get_chunk (io->write_body, io->write_body_offset);
708                         if (!io->write_chunk) {
709                                 soup_message_io_pause (msg);
710                                 return;
711                         }
712                         if (io->write_chunk->length > io->write_length &&
713                             io->write_encoding != SOUP_ENCODING_EOF) {
714                                 /* App is trying to write more than it
715                                  * claimed it would; we have to truncate.
716                                  */
717                                 SoupBuffer *truncated =
718                                         soup_buffer_new_subbuffer (io->write_chunk,
719                                                                    0, io->write_length);
720                                 soup_buffer_free (io->write_chunk);
721                                 io->write_chunk = truncated;
722                         } else if (io->write_encoding == SOUP_ENCODING_EOF &&
723                                    !io->write_chunk->length)
724                                 goto wrote_body;
725                 }
726
727                 if (!write_data (msg, io->write_chunk->data,
728                                  io->write_chunk->length, TRUE))
729                         return;
730
731                 if (io->mode == SOUP_MESSAGE_IO_SERVER ||
732                     priv->msg_flags & SOUP_MESSAGE_CAN_REBUILD)
733                         soup_message_body_wrote_chunk (io->write_body, io->write_chunk);
734                 io->write_body_offset += io->write_chunk->length;
735                 soup_buffer_free (io->write_chunk);
736                 io->write_chunk = NULL;
737
738                 SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
739                 soup_message_wrote_chunk (msg);
740                 SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
741                 break;
742
743         case SOUP_MESSAGE_IO_STATE_CHUNK_SIZE:
744                 if (!io->write_chunk) {
745                         io->write_chunk = soup_message_body_get_chunk (io->write_body, io->write_body_offset);
746                         if (!io->write_chunk) {
747                                 soup_message_io_pause (msg);
748                                 return;
749                         }
750                         g_string_append_printf (io->write_buf, "%lx\r\n",
751                                                 (unsigned long) io->write_chunk->length);
752                         io->write_body_offset += io->write_chunk->length;
753                 }
754
755                 if (!write_data (msg, io->write_buf->str,
756                                  io->write_buf->len, FALSE))
757                         return;
758
759                 g_string_truncate (io->write_buf, 0);
760
761                 if (io->write_chunk->length == 0) {
762                         /* The last chunk has no CHUNK_END... */
763                         io->write_state = SOUP_MESSAGE_IO_STATE_TRAILERS;
764                         break;
765                 }
766
767                 io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK;
768                 /* fall through */
769
770
771         case SOUP_MESSAGE_IO_STATE_CHUNK:
772                 if (!write_data (msg, io->write_chunk->data,
773                                  io->write_chunk->length, TRUE))
774                         return;
775
776                 if (io->mode == SOUP_MESSAGE_IO_SERVER ||
777                     priv->msg_flags & SOUP_MESSAGE_CAN_REBUILD)
778                         soup_message_body_wrote_chunk (io->write_body, io->write_chunk);
779                 soup_buffer_free (io->write_chunk);
780                 io->write_chunk = NULL;
781
782                 io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK_END;
783
784                 SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
785                 soup_message_wrote_chunk (msg);
786                 SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
787
788                 /* fall through */
789
790
791         case SOUP_MESSAGE_IO_STATE_CHUNK_END:
792                 if (!write_data (msg, SOUP_MESSAGE_IO_EOL,
793                                  SOUP_MESSAGE_IO_EOL_LEN, FALSE))
794                         return;
795
796                 io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK_SIZE;
797                 break;
798
799
800         case SOUP_MESSAGE_IO_STATE_TRAILERS:
801                 if (!write_data (msg, SOUP_MESSAGE_IO_EOL,
802                                  SOUP_MESSAGE_IO_EOL_LEN, FALSE))
803                         return;
804
805                 io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
806
807                 SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
808                 soup_message_wrote_body (msg);
809                 SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
810                 /* fall through */
811
812
813         case SOUP_MESSAGE_IO_STATE_FINISHING:
814                 if (io->write_tag) {
815                         g_signal_handler_disconnect (io->sock, io->write_tag);
816                         io->write_tag = 0;
817                 }
818                 io->write_state = SOUP_MESSAGE_IO_STATE_DONE;
819
820                 if (io->mode == SOUP_MESSAGE_IO_CLIENT) {
821                         io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
822                         io_read (sock, msg);
823                 } else
824                         soup_message_io_finished (msg);
825                 return;
826
827
828         case SOUP_MESSAGE_IO_STATE_DONE:
829         default:
830                 g_return_if_reached ();
831         }
832
833         goto write_more;
834 }
835
836 static void
837 io_read (SoupSocket *sock, SoupMessage *msg)
838 {
839         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
840         SoupMessageIOData *io = priv->io_data;
841         guint status;
842
843  read_more:
844         switch (io->read_state) {
845         case SOUP_MESSAGE_IO_STATE_NOT_STARTED:
846                 return;
847
848
849         case SOUP_MESSAGE_IO_STATE_HEADERS:
850                 if (!read_metadata (msg, TRUE))
851                         return;
852
853                 /* We need to "rewind" io->read_meta_buf back one line.
854                  * That SHOULD be two characters (CR LF), but if the
855                  * web server was stupid, it might only be one.
856                  */
857                 if (io->read_meta_buf->len < 3 ||
858                     io->read_meta_buf->data[io->read_meta_buf->len - 2] == '\n')
859                         io->read_meta_buf->len--;
860                 else
861                         io->read_meta_buf->len -= 2;
862                 io->read_meta_buf->data[io->read_meta_buf->len] = '\0';
863                 status = io->parse_headers_cb (msg, (char *)io->read_meta_buf->data,
864                                                io->read_meta_buf->len,
865                                                &io->read_encoding,
866                                                io->header_data);
867                 g_byte_array_set_size (io->read_meta_buf, 0);
868
869                 if (status != SOUP_STATUS_OK) {
870                         /* Either we couldn't parse the headers, or they
871                          * indicated something that would mean we wouldn't
872                          * be able to parse the body. (Eg, unknown
873                          * Transfer-Encoding.). Skip the rest of the
874                          * reading, and make sure the connection gets
875                          * closed when we're done.
876                          */
877                         soup_message_set_status (msg, status);
878                         soup_message_headers_append (msg->request_headers,
879                                                      "Connection", "close");
880                         io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
881                         break;
882                 }
883
884                 if (io->read_encoding == SOUP_ENCODING_EOF)
885                         io->read_eof_ok = TRUE;
886
887                 if (io->read_encoding == SOUP_ENCODING_CONTENT_LENGTH) {
888                         SoupMessageHeaders *hdrs =
889                                 (io->mode == SOUP_MESSAGE_IO_CLIENT) ?
890                                 msg->response_headers : msg->request_headers;
891                         io->read_length = soup_message_headers_get_content_length (hdrs);
892
893                         if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
894                             !soup_message_is_keepalive (msg)) {
895                                 /* Some servers suck and send
896                                  * incorrect Content-Length values, so
897                                  * allow EOF termination in this case
898                                  * (iff the message is too short) too.
899                                  */
900                                 io->read_eof_ok = TRUE;
901                         }
902                 }
903
904                 if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
905                     SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
906                         if (msg->status_code == SOUP_STATUS_CONTINUE &&
907                             io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING) {
908                                 /* Pause the reader, unpause the writer */
909                                 io->read_state =
910                                         SOUP_MESSAGE_IO_STATE_BLOCKING;
911                                 io->write_state =
912                                         io_body_state (io->write_encoding);
913                         } else {
914                                 /* Just stay in HEADERS */
915                                 io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
916                         }
917                 } else if (io->mode == SOUP_MESSAGE_IO_SERVER &&
918                            soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) {
919                         /* The client requested a Continue response. The
920                          * got_headers handler may change this to something
921                          * else though.
922                          */
923                         soup_message_set_status (msg, SOUP_STATUS_CONTINUE);
924                         io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
925                         io->read_state = SOUP_MESSAGE_IO_STATE_BLOCKING;
926                 } else {
927                         io->read_state = io_body_state (io->read_encoding);
928
929                         /* If the client was waiting for a Continue
930                          * but got something else, then it's done
931                          * writing.
932                          */
933                         if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
934                             io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING)
935                                 io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
936                 }
937
938                 if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
939                     SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
940                         SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
941                         soup_message_got_informational (msg);
942                         soup_message_cleanup_response (msg);
943                         SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
944                 } else {
945                         SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
946                         soup_message_got_headers (msg);
947                         SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
948                 }
949                 break;
950
951
952         case SOUP_MESSAGE_IO_STATE_BLOCKING:
953                 io_write (sock, msg);
954
955                 /* As in the io_write case, we *must* return here. */
956                 return;
957
958
959         case SOUP_MESSAGE_IO_STATE_BODY:
960                 if (!read_body_chunk (msg))
961                         return;
962
963         got_body:
964                 if (!io_handle_sniffing (msg, TRUE)) {
965                         /* If the message was paused (as opposed to
966                          * cancelled), we need to make sure we wind up
967                          * back here when it's unpaused, even if it
968                          * was doing a chunked or EOF-terminated read
969                          * before.
970                          */
971                         if (io == priv->io_data) {
972                                 io->read_state = SOUP_MESSAGE_IO_STATE_BODY;
973                                 io->read_encoding = SOUP_ENCODING_CONTENT_LENGTH;
974                                 io->read_length = 0;
975                         }
976                         return;
977                 }
978
979                 io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
980
981                 SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
982                 soup_message_got_body (msg);
983                 SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
984                 break;
985
986
987         case SOUP_MESSAGE_IO_STATE_CHUNK_SIZE:
988                 if (!read_metadata (msg, FALSE))
989                         return;
990
991                 io->read_length = strtoul ((char *)io->read_meta_buf->data, NULL, 16);
992                 g_byte_array_set_size (io->read_meta_buf, 0);
993
994                 if (io->read_length > 0)
995                         io->read_state = SOUP_MESSAGE_IO_STATE_CHUNK;
996                 else
997                         io->read_state = SOUP_MESSAGE_IO_STATE_TRAILERS;
998                 break;
999
1000
1001         case SOUP_MESSAGE_IO_STATE_CHUNK:
1002                 if (!read_body_chunk (msg))
1003                         return;
1004
1005                 io->read_state = SOUP_MESSAGE_IO_STATE_CHUNK_END;
1006                 break;
1007
1008
1009         case SOUP_MESSAGE_IO_STATE_CHUNK_END:
1010                 if (!read_metadata (msg, FALSE))
1011                         return;
1012
1013                 g_byte_array_set_size (io->read_meta_buf, 0);
1014                 io->read_state = SOUP_MESSAGE_IO_STATE_CHUNK_SIZE;
1015                 break;
1016
1017
1018         case SOUP_MESSAGE_IO_STATE_TRAILERS:
1019                 if (!read_metadata (msg, FALSE))
1020                         return;
1021
1022                 if (io->read_meta_buf->len <= SOUP_MESSAGE_IO_EOL_LEN)
1023                         goto got_body;
1024
1025                 /* FIXME: process trailers */
1026                 g_byte_array_set_size (io->read_meta_buf, 0);
1027                 break;
1028
1029
1030         case SOUP_MESSAGE_IO_STATE_FINISHING:
1031                 if (io->read_tag) {
1032                         g_signal_handler_disconnect (io->sock, io->read_tag);
1033                         io->read_tag = 0;
1034                 }
1035                 io->read_state = SOUP_MESSAGE_IO_STATE_DONE;
1036
1037                 if (io->mode == SOUP_MESSAGE_IO_SERVER) {
1038                         io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
1039                         io_write (sock, msg);
1040                 } else
1041                         soup_message_io_finished (msg);
1042                 return;
1043
1044
1045         case SOUP_MESSAGE_IO_STATE_DONE:
1046         default:
1047                 g_return_if_reached ();
1048         }
1049
1050         goto read_more;
1051 }
1052
1053 static void
1054 socket_tls_certificate_changed (GObject *sock, GParamSpec *pspec,
1055                                 gpointer msg)
1056 {
1057         GTlsCertificate *certificate;
1058         GTlsCertificateFlags errors;
1059
1060         g_object_get (sock,
1061                       SOUP_SOCKET_TLS_CERTIFICATE, &certificate,
1062                       SOUP_SOCKET_TLS_ERRORS, &errors,
1063                       NULL);
1064         g_object_set (msg,
1065                       SOUP_MESSAGE_TLS_CERTIFICATE, certificate,
1066                       SOUP_MESSAGE_TLS_ERRORS, errors,
1067                       NULL);
1068         if (certificate)
1069                 g_object_unref (certificate);
1070 }
1071
1072 static SoupMessageIOData *
1073 new_iostate (SoupMessage *msg, SoupSocket *sock, SoupMessageIOMode mode,
1074              SoupMessageGetHeadersFn get_headers_cb,
1075              SoupMessageParseHeadersFn parse_headers_cb,
1076              gpointer header_data,
1077              SoupMessageCompletionFn completion_cb,
1078              gpointer completion_data)
1079 {
1080         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1081         SoupMessageIOData *io;
1082
1083         io = g_slice_new0 (SoupMessageIOData);
1084         io->sock = g_object_ref (sock);
1085         io->mode = mode;
1086         io->get_headers_cb   = get_headers_cb;
1087         io->parse_headers_cb = parse_headers_cb;
1088         io->header_data      = header_data;
1089         io->completion_cb    = completion_cb;
1090         io->completion_data  = completion_data;
1091
1092         io->read_meta_buf    = g_byte_array_new ();
1093         io->write_buf        = g_string_new (NULL);
1094
1095         io->read_tag  = g_signal_connect (io->sock, "readable",
1096                                           G_CALLBACK (io_read), msg);
1097         io->write_tag = g_signal_connect (io->sock, "writable",
1098                                           G_CALLBACK (io_write), msg);
1099
1100         io->read_state  = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
1101         io->write_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
1102
1103         if (soup_socket_is_ssl (io->sock)) {
1104                 io->tls_signal_id = g_signal_connect (io->sock, "notify::tls-certificate",
1105                                                       G_CALLBACK (socket_tls_certificate_changed), msg);
1106         }
1107
1108         if (priv->io_data)
1109                 soup_message_io_cleanup (msg);
1110         priv->io_data = io;
1111         return io;
1112 }
1113
1114 void
1115 soup_message_io_client (SoupMessageQueueItem *item,
1116                         SoupMessageGetHeadersFn get_headers_cb,
1117                         SoupMessageParseHeadersFn parse_headers_cb,
1118                         gpointer header_data,
1119                         SoupMessageCompletionFn completion_cb,
1120                         gpointer completion_data)
1121 {
1122         SoupMessageIOData *io;
1123         SoupSocket *sock = soup_connection_get_socket (item->conn);
1124
1125         io = new_iostate (item->msg, sock, SOUP_MESSAGE_IO_CLIENT,
1126                           get_headers_cb, parse_headers_cb, header_data,
1127                           completion_cb, completion_data);
1128
1129         io->item = item;
1130         soup_message_queue_item_ref (item);
1131         io->cancellable = item->cancellable;
1132
1133         io->read_body       = item->msg->response_body;
1134         io->write_body      = item->msg->request_body;
1135
1136         io->write_state     = SOUP_MESSAGE_IO_STATE_HEADERS;
1137         io_write (sock, item->msg);
1138 }
1139
1140 void
1141 soup_message_io_server (SoupMessage *msg, SoupSocket *sock,
1142                         SoupMessageGetHeadersFn get_headers_cb,
1143                         SoupMessageParseHeadersFn parse_headers_cb,
1144                         gpointer header_data,
1145                         SoupMessageCompletionFn completion_cb,
1146                         gpointer completion_data)
1147 {
1148         SoupMessageIOData *io;
1149
1150         io = new_iostate (msg, sock, SOUP_MESSAGE_IO_SERVER,
1151                           get_headers_cb, parse_headers_cb, header_data,
1152                           completion_cb, completion_data);
1153
1154         io->read_body       = msg->request_body;
1155         io->write_body      = msg->response_body;
1156
1157         io->read_state      = SOUP_MESSAGE_IO_STATE_HEADERS;
1158         io_read (sock, msg);
1159 }
1160
1161 void  
1162 soup_message_io_pause (SoupMessage *msg)
1163 {
1164         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1165         SoupMessageIOData *io = priv->io_data;
1166
1167         g_return_if_fail (io != NULL);
1168
1169         if (io->write_tag) {
1170                 g_signal_handler_disconnect (io->sock, io->write_tag);
1171                 io->write_tag = 0;
1172         }
1173         if (io->read_tag) {
1174                 g_signal_handler_disconnect (io->sock, io->read_tag);
1175                 io->read_tag = 0;
1176         }
1177
1178         if (io->unpause_source) {
1179                 g_source_destroy (io->unpause_source);
1180                 io->unpause_source = NULL;
1181         }
1182 }
1183
1184 static gboolean
1185 io_unpause_internal (gpointer msg)
1186 {
1187         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1188         SoupMessageIOData *io = priv->io_data;
1189
1190         g_return_val_if_fail (io != NULL, FALSE);
1191         io->unpause_source = NULL;
1192
1193         if (io->write_tag || io->read_tag)
1194                 return FALSE;
1195
1196         if (io->write_state != SOUP_MESSAGE_IO_STATE_DONE) {
1197                 io->write_tag = g_signal_connect (io->sock, "writable",
1198                                                   G_CALLBACK (io_write), msg);
1199         }
1200
1201         if (io->read_state != SOUP_MESSAGE_IO_STATE_DONE) {
1202                 io->read_tag = g_signal_connect (io->sock, "readable",
1203                                                  G_CALLBACK (io_read), msg);
1204         }
1205
1206         if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->write_state))
1207                 io_write (io->sock, msg);
1208         else if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->read_state))
1209                 io_read (io->sock, msg);
1210
1211         return FALSE;
1212 }
1213
1214 void
1215 soup_message_io_unpause (SoupMessage *msg)
1216 {
1217         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1218         SoupMessageIOData *io = priv->io_data;
1219         gboolean non_blocking;
1220         GMainContext *async_context;
1221
1222         g_return_if_fail (io != NULL);
1223
1224         g_object_get (io->sock,
1225                       SOUP_SOCKET_FLAG_NONBLOCKING, &non_blocking,
1226                       SOUP_SOCKET_ASYNC_CONTEXT, &async_context,
1227                       NULL);
1228         if (non_blocking) {
1229                 if (!io->unpause_source) {
1230                         io->unpause_source = soup_add_completion (
1231                                 async_context, io_unpause_internal, msg);
1232                 }
1233         } else
1234                 io_unpause_internal (msg);
1235         if (async_context)
1236                 g_main_context_unref (async_context);
1237 }
1238
1239 /**
1240  * soup_message_io_in_progress:
1241  * @msg: a #SoupMessage
1242  *
1243  * Tests whether or not I/O is currently in progress on @msg.
1244  *
1245  * Return value: whether or not I/O is currently in progress.
1246  **/
1247 gboolean
1248 soup_message_io_in_progress (SoupMessage *msg)
1249 {
1250         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1251
1252         return priv->io_data != NULL;
1253 }