source: trunk/third/libsoup/libsoup/soup-message-io.c @ 21519

Revision 21519, 20.3 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21518, which included commits to RCS files with non-trunk default branches.
Line 
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-message.h"
16#include "soup-message-private.h"
17#include "soup-socket.h"
18#include "soup-ssl.h"
19
20typedef enum {
21        SOUP_MESSAGE_IO_CLIENT,
22        SOUP_MESSAGE_IO_SERVER
23} SoupMessageIOMode;
24
25typedef enum {
26        SOUP_MESSAGE_IO_STATE_NOT_STARTED,
27        SOUP_MESSAGE_IO_STATE_HEADERS,
28        SOUP_MESSAGE_IO_STATE_BLOCKING,
29        SOUP_MESSAGE_IO_STATE_BODY,
30        SOUP_MESSAGE_IO_STATE_CHUNK_SIZE,
31        SOUP_MESSAGE_IO_STATE_CHUNK,
32        SOUP_MESSAGE_IO_STATE_CHUNK_END,
33        SOUP_MESSAGE_IO_STATE_TRAILERS,
34        SOUP_MESSAGE_IO_STATE_DONE
35} SoupMessageIOState;
36
37typedef struct {
38        SoupSocket           *sock;
39        SoupMessageIOMode     mode;
40
41        SoupMessageIOState    read_state;
42        SoupTransferEncoding  read_encoding;
43        GByteArray           *read_buf;
44        GByteArray           *read_meta_buf;
45        SoupDataBuffer       *read_body;
46        guint                 read_length;
47
48        SoupMessageIOState    write_state;
49        SoupTransferEncoding  write_encoding;
50        GString              *write_buf;
51        SoupDataBuffer       *write_body;
52        guint                 written;
53
54        guint read_tag, write_tag, err_tag;
55
56        SoupMessageGetHeadersFn   get_headers_cb;
57        SoupMessageParseHeadersFn parse_headers_cb;
58        gpointer                  user_data;
59} SoupMessageIOData;
60       
61
62/* Put these around callback invocation if there is code afterward
63 * that depends on the IO having not been cancelled.
64 */
65#define dummy_to_make_emacs_happy {
66#define SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK { gboolean cancelled; g_object_ref (msg);
67#define SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED cancelled = (msg->priv->io_data != io); g_object_unref (msg); if (cancelled || !io->read_tag || !io->write_tag) return; }
68#define SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED(val) cancelled = (msg->priv->io_data != io); g_object_unref (msg); if (cancelled || !io->read_tag || !io->write_tag) return val; }
69
70#define RESPONSE_BLOCK_SIZE 8192
71
72static void
73io_cleanup (SoupMessage *msg)
74{
75        SoupMessageIOData *io = msg->priv->io_data;
76
77        if (!io)
78                return;
79
80        soup_message_io_stop (msg);
81
82        if (io->sock)
83                g_object_unref (io->sock);
84
85        if (io->read_buf)
86                g_byte_array_free (io->read_buf, TRUE);
87        g_byte_array_free (io->read_meta_buf, TRUE);
88
89        g_string_free (io->write_buf, TRUE);
90
91        g_free (io);
92        msg->priv->io_data = NULL;
93}
94
95/**
96 * soup_message_io_stop:
97 * @msg: a #SoupMessage
98 *
99 * Immediately stops I/O on msg; if the connection would be left in an
100 * inconsistent state, it will be closed.
101 **/
102void
103soup_message_io_stop (SoupMessage *msg)
104{
105        SoupMessageIOData *io = msg->priv->io_data;
106
107        if (!io)
108                return;
109
110        if (io->read_tag) {
111                g_signal_handler_disconnect (io->sock, io->read_tag);
112                io->read_tag = 0;
113        }
114        if (io->write_tag) {
115                g_signal_handler_disconnect (io->sock, io->write_tag);
116                io->write_tag = 0;
117        }
118        if (io->err_tag) {
119                g_signal_handler_disconnect (io->sock, io->err_tag);
120                io->err_tag = 0;
121        }
122
123        if (io->read_state != SOUP_MESSAGE_IO_STATE_DONE)
124                soup_socket_disconnect (io->sock);
125}
126
127#define SOUP_MESSAGE_IO_EOL            "\r\n"
128#define SOUP_MESSAGE_IO_EOL_LEN        2
129#define SOUP_MESSAGE_IO_DOUBLE_EOL     "\r\n\r\n"
130#define SOUP_MESSAGE_IO_DOUBLE_EOL_LEN 4
131
132static void
133soup_message_io_finished (SoupMessage *msg)
134{
135        g_object_ref (msg);
136        io_cleanup (msg);
137        if (SOUP_MESSAGE_IS_STARTING (msg))
138                soup_message_restarted (msg);
139        else
140                soup_message_finished (msg);
141        g_object_unref (msg);
142}
143
144
145static void
146io_error (SoupSocket *sock, SoupMessage *msg)
147{
148        SoupMessageIOData *io = msg->priv->io_data;
149
150        /* Closing the connection to signify EOF is sometimes ok */
151        if (io->read_state == SOUP_MESSAGE_IO_STATE_BODY &&
152            io->read_encoding == SOUP_TRANSFER_UNKNOWN) {
153                io->read_state = SOUP_MESSAGE_IO_STATE_DONE;
154                soup_message_io_finished (msg);
155                return;
156        }
157
158        if (!SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
159                GError *err = g_object_get_data (G_OBJECT (sock),
160                                                 "SoupSocket-last_error");
161
162                if (err && err->domain == SOUP_SSL_ERROR) {
163                        soup_message_set_status_full (msg,
164                                                      SOUP_STATUS_SSL_FAILED,
165                                                      err->message);
166                } else
167                        soup_message_set_status (msg, SOUP_STATUS_IO_ERROR);
168        }
169
170        soup_message_io_finished (msg);
171}
172
173static gboolean
174read_metadata (SoupMessage *msg, const char *boundary)
175{
176        SoupMessageIOData *io = msg->priv->io_data;
177        SoupSocketIOStatus status;
178        char read_buf[RESPONSE_BLOCK_SIZE];
179        guint boundary_len = strlen (boundary);
180        gsize nread;
181        gboolean done;
182
183        do {
184                status = soup_socket_read_until (io->sock, read_buf,
185                                                 sizeof (read_buf),
186                                                 boundary, boundary_len,
187                                                 &nread, &done);
188                switch (status) {
189                case SOUP_SOCKET_OK:
190                        g_byte_array_append (io->read_meta_buf, read_buf, nread);
191                        break;
192
193                case SOUP_SOCKET_ERROR:
194                case SOUP_SOCKET_EOF:
195                        io_error (io->sock, msg);
196                        return FALSE;
197
198                case SOUP_SOCKET_WOULD_BLOCK:
199                        return FALSE;
200                }
201        } while (!done);
202
203        return TRUE;
204}
205
206static gboolean
207read_body_chunk (SoupMessage *msg)
208{
209        SoupMessageIOData *io = msg->priv->io_data;
210        SoupSocketIOStatus status;
211        char read_buf[RESPONSE_BLOCK_SIZE];
212        guint len = sizeof (read_buf);
213        gboolean read_to_eof = (io->read_encoding == SOUP_TRANSFER_UNKNOWN);
214        gsize nread;
215
216        while (read_to_eof || io->read_length > 0) {
217                if (!read_to_eof)
218                        len = MIN (len, io->read_length);
219
220                status = soup_socket_read (io->sock, read_buf, len, &nread);
221
222                switch (status) {
223                case SOUP_SOCKET_OK:
224                        if (!nread)
225                                break;
226
227                        io->read_body->owner  = SOUP_BUFFER_STATIC;
228                        io->read_body->body   = read_buf;
229                        io->read_body->length = nread;
230
231                        SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
232                        soup_message_got_chunk (msg);
233                        SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
234
235                        memset (io->read_body, 0, sizeof (SoupDataBuffer));
236
237                        if (io->read_buf)
238                                g_byte_array_append (io->read_buf, read_buf, nread);
239                        io->read_length -= nread;
240                        break;
241
242                case SOUP_SOCKET_EOF:
243                        if (read_to_eof)
244                                return TRUE;
245                        /* else fall through */
246
247                case SOUP_SOCKET_ERROR:
248                        io_error (io->sock, msg);
249                        return FALSE;
250
251                case SOUP_SOCKET_WOULD_BLOCK:
252                        return FALSE;
253                }
254        }
255
256        return TRUE;
257}
258
259static gboolean
260write_data (SoupMessage *msg, const char *data, guint len)
261{
262        SoupMessageIOData *io = msg->priv->io_data;
263        SoupSocketIOStatus status;
264        gsize nwrote;
265
266        while (len > io->written) {
267                status = soup_socket_write (io->sock,
268                                            data + io->written,
269                                            len - io->written,
270                                            &nwrote);
271                switch (status) {
272                case SOUP_SOCKET_EOF:
273                case SOUP_SOCKET_ERROR:
274                        io_error (io->sock, msg);
275                        return FALSE;
276
277                case SOUP_SOCKET_WOULD_BLOCK:
278                        return FALSE;
279
280                case SOUP_SOCKET_OK:
281                        io->written += nwrote;
282                        break;
283                }
284        }
285
286        io->written = 0;
287        return TRUE;
288}
289
290static inline SoupMessageIOState
291io_body_state (SoupTransferEncoding encoding)
292{
293        if (encoding == SOUP_TRANSFER_CHUNKED)
294                return SOUP_MESSAGE_IO_STATE_CHUNK_SIZE;
295        else
296                return SOUP_MESSAGE_IO_STATE_BODY;
297}
298
299static void io_read (SoupSocket *sock, SoupMessage *msg);
300
301/*
302 * There are two request/response formats: the basic request/response,
303 * possibly with one or more unsolicited informational responses (such
304 * as the WebDAV "102 Processing" response):
305 *
306 *     Client                            Server
307 *      W:HEADERS  / R:NOT_STARTED    ->  R:HEADERS  / W:NOT_STARTED
308 *      W:BODY     / R:NOT_STARTED    ->  R:BODY     / W:NOT_STARTED
309 *     [W:DONE     / R:HEADERS (1xx)  <-  R:DONE     / W:HEADERS (1xx) ...]
310 *      W:DONE     / R:HEADERS        <-  R:DONE     / W:HEADERS
311 *      W:DONE     / R:BODY           <-  R:DONE     / W:BODY
312 *      W:DONE     / R:DONE               R:DONE     / W:DONE
313 *     
314 * and the "Expect: 100-continue" request/response, in which each
315 * writer has to pause and wait for the other at some point:
316 *
317 *     Client                            Server
318 *      W:HEADERS  / R:NOT_STARTED    ->  R:HEADERS  / W:NOT_STARTED
319 *      W:BLOCKING / R:HEADERS (100)  <-  R:BLOCKING / W:HEADERS (100)
320 *      W:BODY     / R:BLOCKING       ->  R:BODY     / W:BLOCKING
321 *      W:DONE     / R:HEADERS        <-  R:DONE     / W:HEADERS
322 *      W:DONE     / R:BODY           <-  R:DONE     / W:BODY
323 *      W:DONE     / R:DONE               R:DONE     / W:DONE
324 */
325
326static void
327io_write (SoupSocket *sock, SoupMessage *msg)
328{
329        SoupMessageIOData *io = msg->priv->io_data;
330
331 write_more:
332        switch (io->write_state) {
333        case SOUP_MESSAGE_IO_STATE_NOT_STARTED:
334                return;
335
336
337        case SOUP_MESSAGE_IO_STATE_HEADERS:
338                if (!io->write_buf->len) {
339                        io->get_headers_cb (msg, io->write_buf,
340                                            &io->write_encoding,
341                                            io->user_data);
342                        if (!io->write_buf->len) {
343                                soup_message_io_pause (msg);
344                                return;
345                        }
346                }
347
348                if (!write_data (msg, io->write_buf->str, io->write_buf->len))
349                        return;
350
351                g_string_truncate (io->write_buf, 0);
352
353                if (io->mode == SOUP_MESSAGE_IO_SERVER &&
354                    SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
355                        if (msg->status_code == SOUP_STATUS_CONTINUE) {
356                                /* Stop and wait for the body now */
357                                io->write_state =
358                                        SOUP_MESSAGE_IO_STATE_BLOCKING;
359                        } else {
360                                /* We just wrote a 1xx response
361                                 * header, so stay in STATE_HEADERS.
362                                 * (The caller will pause us from the
363                                 * wrote_informational callback if he
364                                 * is not ready to send the final
365                                 * response.)
366                                 */
367                        }
368                } else if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
369                           msg->priv->msg_flags & SOUP_MESSAGE_EXPECT_CONTINUE) {
370                        /* Need to wait for the Continue response */
371                        io->write_state = SOUP_MESSAGE_IO_STATE_BLOCKING;
372                        io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
373                } else
374                        io->write_state = io_body_state (io->write_encoding);
375
376                SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
377                if (SOUP_STATUS_IS_INFORMATIONAL (msg->status_code))
378                        soup_message_wrote_informational (msg);
379                else
380                        soup_message_wrote_headers (msg);
381                SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
382                break;
383
384
385        case SOUP_MESSAGE_IO_STATE_BLOCKING:
386                io_read (sock, msg);
387
388                /* If io_read reached a point where we could write
389                 * again, it would have recursively called io_write.
390                 * So (a) we don't need to try to keep writing, and
391                 * (b) we can't anyway, because msg may have been
392                 * destroyed.
393                 */
394                return;
395
396
397        case SOUP_MESSAGE_IO_STATE_BODY:
398                if (!write_data (msg, io->write_body->body,
399                                 io->write_body->length))
400                        return;
401
402                io->write_state = SOUP_MESSAGE_IO_STATE_DONE;
403
404                SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
405                soup_message_wrote_body (msg);
406                SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
407                break;
408
409
410        case SOUP_MESSAGE_IO_STATE_CHUNK_SIZE:
411                if (!io->write_buf->len) {
412                        SoupDataBuffer *chunk;
413
414                        SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
415                        chunk = soup_message_pop_chunk (msg);
416                        SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
417
418                        if (!chunk) {
419                                soup_message_io_pause (msg);
420                                return;
421                        }
422                        memcpy (io->write_body, chunk, sizeof (SoupDataBuffer));
423                        g_free (chunk);
424
425                        g_string_append_printf (io->write_buf, "%x\r\n",
426                                                io->write_body->length);
427                }
428
429                if (!write_data (msg, io->write_buf->str, io->write_buf->len))
430                        return;
431
432                g_string_truncate (io->write_buf, 0);
433
434                if (io->write_body->length == 0) {
435                        /* The last chunk has no CHUNK_END... */
436                        io->write_state = SOUP_MESSAGE_IO_STATE_TRAILERS;
437                        break;
438                }
439
440                io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK;
441                /* fall through */
442
443
444        case SOUP_MESSAGE_IO_STATE_CHUNK:
445                if (!write_data (msg, io->write_body->body,
446                                 io->write_body->length))
447                        return;
448
449                io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK_END;
450
451                SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
452                soup_message_wrote_chunk (msg);
453                SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
454                /* fall through */
455
456
457        case SOUP_MESSAGE_IO_STATE_CHUNK_END:
458                if (!write_data (msg, SOUP_MESSAGE_IO_EOL,
459                                 SOUP_MESSAGE_IO_EOL_LEN))
460                        return;
461
462                if (io->write_body->owner == SOUP_BUFFER_SYSTEM_OWNED)
463                        g_free (io->write_body->body);
464                memset (io->write_body, 0, sizeof (SoupDataBuffer));
465
466                io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK_SIZE;
467                break;
468
469
470        case SOUP_MESSAGE_IO_STATE_TRAILERS:
471                if (!write_data (msg, SOUP_MESSAGE_IO_EOL,
472                                 SOUP_MESSAGE_IO_EOL_LEN))
473                        return;
474
475                io->write_state = SOUP_MESSAGE_IO_STATE_DONE;
476
477                SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
478                soup_message_wrote_body (msg);
479                SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
480                /* fall through */
481
482
483        case SOUP_MESSAGE_IO_STATE_DONE:
484                if (io->mode == SOUP_MESSAGE_IO_CLIENT) {
485                        io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
486                        io_read (sock, msg);
487                } else
488                        soup_message_io_finished (msg);
489                return;
490
491
492        default:
493                g_return_if_reached ();
494        }
495
496        goto write_more;
497}
498
499static void
500io_read (SoupSocket *sock, SoupMessage *msg)
501{
502        SoupMessageIOData *io = msg->priv->io_data;
503        guint status;
504
505 read_more:
506        switch (io->read_state) {
507        case SOUP_MESSAGE_IO_STATE_NOT_STARTED:
508                return;
509
510
511        case SOUP_MESSAGE_IO_STATE_HEADERS:
512                if (!read_metadata (msg, SOUP_MESSAGE_IO_DOUBLE_EOL))
513                        return;
514
515                io->read_meta_buf->len -= SOUP_MESSAGE_IO_EOL_LEN;
516                io->read_meta_buf->data[io->read_meta_buf->len] = '\0';
517                status = io->parse_headers_cb (msg, io->read_meta_buf->data,
518                                               io->read_meta_buf->len,
519                                               &io->read_encoding,
520                                               &io->read_length,
521                                               io->user_data);
522                g_byte_array_set_size (io->read_meta_buf, 0);
523
524                if (status != SOUP_STATUS_OK) {
525                        /* Either we couldn't parse the headers, or they
526                         * indicated something that would mean we wouldn't
527                         * be able to parse the body. (Eg, unknown
528                         * Transfer-Encoding.). Skip the rest of the
529                         * reading, and make sure the connection gets
530                         * closed when we're done.
531                         */
532                        soup_message_set_status (msg, status);
533                        soup_message_add_header (msg->request_headers,
534                                                 "Connection", "close");
535                        io->read_state = SOUP_MESSAGE_IO_STATE_DONE;
536                        break;
537                }
538
539                if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
540                    SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
541                        if (msg->status_code == SOUP_STATUS_CONTINUE &&
542                            io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING) {
543                                /* Pause the reader, unpause the writer */
544                                io->read_state =
545                                        SOUP_MESSAGE_IO_STATE_BLOCKING;
546                                io->write_state =
547                                        io_body_state (io->write_encoding);
548                        } else {
549                                /* Just stay in HEADERS */
550                                io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
551                        }
552                } else if (io->mode == SOUP_MESSAGE_IO_SERVER &&
553                           (msg->priv->msg_flags & SOUP_MESSAGE_EXPECT_CONTINUE)) {
554                        /* The client requested a Continue response. */
555                        io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
556                        io->read_state = SOUP_MESSAGE_IO_STATE_BLOCKING;
557                } else
558                        io->read_state = io_body_state (io->read_encoding);
559
560                SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
561                if (SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
562                        soup_message_got_informational (msg);
563                        soup_message_cleanup_response (msg);
564                } else
565                        soup_message_got_headers (msg);
566                SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
567                break;
568
569
570        case SOUP_MESSAGE_IO_STATE_BLOCKING:
571                io_write (sock, msg);
572
573                /* As in the io_write case, we *must* return here. */
574                return;
575
576
577        case SOUP_MESSAGE_IO_STATE_BODY:
578                if (!read_body_chunk (msg))
579                        return;
580
581        got_body:
582                if (io->read_buf) {
583                        io->read_body->owner = SOUP_BUFFER_SYSTEM_OWNED;
584                        io->read_body->body = io->read_buf->data;
585                        io->read_body->length = io->read_buf->len;
586
587                        g_byte_array_free (io->read_buf, FALSE);
588                        io->read_buf = NULL;
589                }
590
591                io->read_state = SOUP_MESSAGE_IO_STATE_DONE;
592
593                SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
594                soup_message_got_body (msg);
595                SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
596                break;
597
598
599        case SOUP_MESSAGE_IO_STATE_CHUNK_SIZE:
600                if (!read_metadata (msg, SOUP_MESSAGE_IO_EOL))
601                        return;
602
603                io->read_length = strtoul (io->read_meta_buf->data, NULL, 16);
604                g_byte_array_set_size (io->read_meta_buf, 0);
605
606                if (io->read_length > 0)
607                        io->read_state = SOUP_MESSAGE_IO_STATE_CHUNK;
608                else
609                        io->read_state = SOUP_MESSAGE_IO_STATE_TRAILERS;
610                break;
611
612
613        case SOUP_MESSAGE_IO_STATE_CHUNK:
614                if (!read_body_chunk (msg))
615                        return;
616
617                io->read_state = SOUP_MESSAGE_IO_STATE_CHUNK_END;
618                break;
619
620
621        case SOUP_MESSAGE_IO_STATE_CHUNK_END:
622                if (!read_metadata (msg, SOUP_MESSAGE_IO_EOL))
623                        return;
624
625                g_byte_array_set_size (io->read_meta_buf, 0);
626                io->read_state = SOUP_MESSAGE_IO_STATE_CHUNK_SIZE;
627                break;
628
629
630        case SOUP_MESSAGE_IO_STATE_TRAILERS:
631                if (!read_metadata (msg, SOUP_MESSAGE_IO_EOL))
632                        return;
633
634                if (io->read_meta_buf->len == SOUP_MESSAGE_IO_EOL_LEN)
635                        goto got_body;
636
637                /* FIXME: process trailers */
638                g_byte_array_set_size (io->read_meta_buf, 0);
639                break;
640
641
642        case SOUP_MESSAGE_IO_STATE_DONE:
643                if (io->mode == SOUP_MESSAGE_IO_SERVER) {
644                        io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
645                        io_write (sock, msg);
646                } else
647                        soup_message_io_finished (msg);
648                return;
649
650
651        default:
652                g_return_if_reached ();
653        }
654
655        goto read_more;
656}
657
658static SoupMessageIOData *
659new_iostate (SoupMessage *msg, SoupSocket *sock, SoupMessageIOMode mode,
660             SoupMessageGetHeadersFn get_headers_cb,
661             SoupMessageParseHeadersFn parse_headers_cb,
662             gpointer user_data)
663{
664        SoupMessageIOData *io;
665
666        io = g_new0 (SoupMessageIOData, 1);
667        io->sock = g_object_ref (sock);
668        io->mode = mode;
669        io->get_headers_cb   = get_headers_cb;
670        io->parse_headers_cb = parse_headers_cb;
671        io->user_data        = user_data;
672
673        io->read_encoding    = SOUP_TRANSFER_UNKNOWN;
674        io->write_encoding   = SOUP_TRANSFER_UNKNOWN;
675
676        io->read_meta_buf    = g_byte_array_new ();
677        if (!(msg->priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS))
678                io->read_buf = g_byte_array_new ();
679        io->write_buf        = g_string_new (NULL);
680
681        io->read_tag  = g_signal_connect (io->sock, "readable",
682                                          G_CALLBACK (io_read), msg);
683        io->write_tag = g_signal_connect (io->sock, "writable",
684                                          G_CALLBACK (io_write), msg);
685        io->err_tag   = g_signal_connect (io->sock, "disconnected",
686                                          G_CALLBACK (io_error), msg);
687
688        io->read_state  = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
689        io->write_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
690
691        if (msg->priv->io_data)
692                io_cleanup (msg);
693        msg->priv->io_data = io;
694        return io;
695}
696
697/**
698 * soup_message_io_client:
699 * @msg: a #SoupMessage
700 * @sock: socket to send @msg across
701 * @get_headers_cb: callback function to generate request headers
702 * @parse_headers_cb: callback function to parse response headers
703 * @user_data: data to pass to the callbacks
704 *
705 * Begins the process of sending @msg across @sock.
706 *
707 * Don't call this. Use soup_message_send_request().
708 **/
709void
710soup_message_io_client (SoupMessage *msg, SoupSocket *sock,
711                        SoupMessageGetHeadersFn get_headers_cb,
712                        SoupMessageParseHeadersFn parse_headers_cb,
713                        gpointer user_data)
714{
715        SoupMessageIOData *io;
716
717        io = new_iostate (msg, sock, SOUP_MESSAGE_IO_CLIENT,
718                          get_headers_cb, parse_headers_cb, user_data);
719
720        io->read_body       = &msg->response;
721        io->write_body      = &msg->request;
722
723        io->write_state     = SOUP_MESSAGE_IO_STATE_HEADERS;
724        io_write (sock, msg);
725}
726
727/**
728 * soup_message_io_server:
729 * @msg: an empty #SoupServerMessage
730 * @sock: socket to receive a request on
731 * @get_headers_cb: callback function to generate response headers
732 * @parse_headers_cb: callback function to parse request headers
733 * @user_data: data to pass to the callbacks
734 *
735 * Begins the process of receiving a request from @sock into @msg.
736 *
737 * Don't use this. Use soup_message_receive_request() instead.
738 **/
739void
740soup_message_io_server (SoupMessage *msg, SoupSocket *sock,
741                        SoupMessageGetHeadersFn get_headers_cb,
742                        SoupMessageParseHeadersFn parse_headers_cb,
743                        gpointer user_data)
744{
745        SoupMessageIOData *io;
746
747        io = new_iostate (msg, sock, SOUP_MESSAGE_IO_SERVER,
748                          get_headers_cb, parse_headers_cb, user_data);
749
750        io->read_body       = &msg->request;
751        io->write_body      = &msg->response;
752
753        io->read_state      = SOUP_MESSAGE_IO_STATE_HEADERS;
754        io_read (sock, msg);
755}
756
757/**
758 * soup_message_io_pause:
759 * @msg: a #SoupMessage
760 *
761 * Pauses I/O on @msg.
762 **/
763void 
764soup_message_io_pause (SoupMessage *msg)
765{
766        SoupMessageIOData *io = msg->priv->io_data;
767
768        g_return_if_fail (io != NULL);
769
770        if (io->write_tag) {
771                g_signal_handler_disconnect (io->sock, io->write_tag);
772                io->write_tag = 0;
773        }
774        if (io->read_tag) {
775                g_signal_handler_disconnect (io->sock, io->read_tag);
776                io->read_tag = 0;
777        }
778}
779
780/**
781 * soup_message_io_unpause:
782 * @msg: a #SoupMessage
783 *
784 * Resumes I/O on @msg.
785 **/
786void 
787soup_message_io_unpause (SoupMessage *msg)
788{
789        SoupMessageIOData *io = msg->priv->io_data;
790
791        g_return_if_fail (io != NULL);
792
793        if (io->write_tag || io->read_tag)
794                return;
795
796        io->write_tag = g_signal_connect (io->sock, "writable",
797                                          G_CALLBACK (io_write), msg);
798        io->read_tag = g_signal_connect (io->sock, "readable",
799                                         G_CALLBACK (io_read), msg);
800
801        if (io->write_state != SOUP_MESSAGE_IO_STATE_NOT_STARTED &&
802            io->write_state != SOUP_MESSAGE_IO_STATE_BLOCKING)
803                io_write (io->sock, msg);
804        else
805                io_read (io->sock, msg);
806}
Note: See TracBrowser for help on using the repository browser.