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