1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-message-io.c: HTTP message I/O
5 * Copyright (C) 2000-2003, Ximian, Inc.
12 #include <glib/gi18n-lib.h>
15 #include "soup-body-input-stream.h"
16 #include "soup-body-output-stream.h"
17 #include "soup-client-input-stream.h"
18 #include "soup-connection.h"
19 #include "soup-content-processor.h"
20 #include "soup-content-sniffer-stream.h"
21 #include "soup-filter-input-stream.h"
22 #include "soup-message-private.h"
23 #include "soup-message-queue.h"
24 #include "soup-misc-private.h"
27 #if ENABLE(TIZEN_PERFORMANCE_TEST_LOG)
28 #include <sys/prctl.h>
30 #ifndef PR_TASK_PERF_USER_TRACE
31 #define PR_TASK_PERF_USER_TRACE 666
34 #define MAX_STRING_LEN 256
36 static void prctl_with_url(const char *prestr, const char *url)
38 char s[MAX_STRING_LEN] = "";
40 int len_pre = strlen(prestr);
41 int len_url = strlen(url);
43 strncpy(s, prestr, len_pre);
44 if(len_pre + len_url < len_max) {
45 strncpy(s+len_pre, url, len_url);
48 int len_part = len_max - len_pre - 10;
49 strncpy(s+len_pre, url, len_part);
50 strncpy(s+len_pre+len_part, "...", MAX_STRING_LEN-len_pre-len_part-1);
51 strncpy(s+len_pre+len_part+3, url+len_url-7, 7);
53 prctl(PR_TASK_PERF_USER_TRACE, s, strlen(s));
56 static void prctl_with_url_and_free(const char *prestr, char *url)
58 prctl_with_url(prestr, url);
64 SOUP_MESSAGE_IO_CLIENT,
65 SOUP_MESSAGE_IO_SERVER
69 SOUP_MESSAGE_IO_STATE_NOT_STARTED,
70 SOUP_MESSAGE_IO_STATE_ANY = SOUP_MESSAGE_IO_STATE_NOT_STARTED,
71 SOUP_MESSAGE_IO_STATE_HEADERS,
72 SOUP_MESSAGE_IO_STATE_BLOCKING,
73 SOUP_MESSAGE_IO_STATE_BODY_START,
74 SOUP_MESSAGE_IO_STATE_BODY,
75 SOUP_MESSAGE_IO_STATE_BODY_DATA,
76 SOUP_MESSAGE_IO_STATE_BODY_DONE,
77 SOUP_MESSAGE_IO_STATE_FINISHING,
78 SOUP_MESSAGE_IO_STATE_DONE
81 #define SOUP_MESSAGE_IO_STATE_ACTIVE(state) \
82 (state != SOUP_MESSAGE_IO_STATE_NOT_STARTED && \
83 state != SOUP_MESSAGE_IO_STATE_BLOCKING && \
84 state != SOUP_MESSAGE_IO_STATE_DONE)
85 #define SOUP_MESSAGE_IO_STATE_POLLABLE(state) \
86 (SOUP_MESSAGE_IO_STATE_ACTIVE (state) && \
87 state != SOUP_MESSAGE_IO_STATE_BODY_DONE)
90 SoupMessageQueueItem *item;
91 SoupMessageIOMode mode;
92 GCancellable *cancellable;
95 SoupFilterInputStream *istream;
96 GInputStream *body_istream;
97 GOutputStream *ostream;
98 GOutputStream *body_ostream;
99 GMainContext *async_context;
101 SoupMessageIOState read_state;
102 SoupEncoding read_encoding;
103 GByteArray *read_header_buf;
104 SoupMessageBody *read_body;
107 SoupMessageIOState write_state;
108 SoupEncoding write_encoding;
110 SoupMessageBody *write_body;
111 SoupBuffer *write_chunk;
112 goffset write_body_offset;
113 goffset write_length;
117 GSource *unpause_source;
120 SoupMessageGetHeadersFn get_headers_cb;
121 SoupMessageParseHeadersFn parse_headers_cb;
122 gpointer header_data;
123 SoupMessageCompletionFn completion_cb;
124 gpointer completion_data;
127 #if ENABLE(TIZEN_USE_EXPANDED_RESPONSE_BLOCK)
128 #define RESPONSE_BLOCK_SIZE 32768
130 #define RESPONSE_BLOCK_SIZE 8192
134 soup_message_io_cleanup (SoupMessage *msg)
136 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
137 SoupMessageIOData *io;
139 soup_message_io_stop (msg);
144 priv->io_data = NULL;
147 g_object_unref (io->iostream);
148 if (io->body_istream)
149 g_object_unref (io->body_istream);
150 if (io->body_ostream)
151 g_object_unref (io->body_ostream);
152 if (io->async_context)
153 g_main_context_unref (io->async_context);
155 soup_message_queue_item_unref (io->item);
157 g_byte_array_free (io->read_header_buf, TRUE);
159 g_string_free (io->write_buf, TRUE);
161 soup_buffer_free (io->write_chunk);
163 g_slice_free (SoupMessageIOData, io);
167 soup_message_io_stop (SoupMessage *msg)
169 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
170 SoupMessageIOData *io = priv->io_data;
176 g_source_destroy (io->io_source);
177 g_source_unref (io->io_source);
178 io->io_source = NULL;
181 if (io->unpause_source) {
182 g_source_destroy (io->unpause_source);
183 g_source_unref (io->unpause_source);
184 io->unpause_source = NULL;
187 if (io->mode == SOUP_MESSAGE_IO_SERVER) {
188 if (io->write_state < SOUP_MESSAGE_IO_STATE_FINISHING)
189 g_io_stream_close (io->iostream, NULL, NULL);
194 soup_message_io_finished (SoupMessage *msg)
196 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
197 SoupMessageIOData *io = priv->io_data;
198 SoupMessageCompletionFn completion_cb;
199 gpointer completion_data;
204 completion_cb = io->completion_cb;
205 completion_data = io->completion_data;
208 soup_message_io_cleanup (msg);
210 completion_cb (msg, completion_data);
211 g_object_unref (msg);
215 read_headers (SoupMessage *msg, gboolean blocking,
216 GCancellable *cancellable, GError **error)
218 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
219 SoupMessageIOData *io = priv->io_data;
220 gssize nread, old_len;
224 old_len = io->read_header_buf->len;
225 g_byte_array_set_size (io->read_header_buf, old_len + RESPONSE_BLOCK_SIZE);
226 nread = soup_filter_input_stream_read_line (io->istream,
227 io->read_header_buf->data + old_len,
232 io->read_header_buf->len = old_len + MAX (nread, 0);
234 soup_message_set_status (msg, SOUP_STATUS_MALFORMED);
235 g_set_error_literal (error, G_IO_ERROR,
236 G_IO_ERROR_PARTIAL_INPUT,
237 _("Connection terminated unexpectedly"));
243 if (nread == 1 && old_len >= 2 &&
244 !strncmp ((char *)io->read_header_buf->data +
245 io->read_header_buf->len - 2,
248 else if (nread == 2 && old_len >= 3 &&
249 !strncmp ((char *)io->read_header_buf->data +
250 io->read_header_buf->len - 3,
256 /* We need to "rewind" io->read_header_buf back one line.
257 * That SHOULD be two characters (CR LF), but if the
258 * web server was stupid, it might only be one.
260 if (io->read_header_buf->len < 3 ||
261 io->read_header_buf->data[io->read_header_buf->len - 2] == '\n')
262 io->read_header_buf->len--;
264 io->read_header_buf->len -= 2;
265 io->read_header_buf->data[io->read_header_buf->len] = '\0';
271 processing_stage_cmp (gconstpointer a,
274 SoupProcessingStage stage_a = soup_content_processor_get_processing_stage (SOUP_CONTENT_PROCESSOR (a));
275 SoupProcessingStage stage_b = soup_content_processor_get_processing_stage (SOUP_CONTENT_PROCESSOR (b));
277 if (stage_a > stage_b)
279 if (stage_a == stage_b)
285 soup_message_setup_body_istream (GInputStream *body_stream,
287 SoupSession *session,
288 SoupProcessingStage start_at_stage)
290 GInputStream *istream;
291 GSList *p, *processors;
293 istream = g_object_ref (body_stream);
295 processors = soup_session_get_features (session, SOUP_TYPE_CONTENT_PROCESSOR);
296 processors = g_slist_sort (processors, processing_stage_cmp);
298 for (p = processors; p; p = p->next) {
299 GInputStream *wrapper;
300 SoupContentProcessor *processor;
302 processor = SOUP_CONTENT_PROCESSOR (p->data);
303 if (soup_message_disables_feature (msg, p->data) ||
304 soup_content_processor_get_processing_stage (processor) < start_at_stage)
307 wrapper = soup_content_processor_wrap_input (processor, istream, msg, NULL);
309 g_object_unref (istream);
314 g_slist_free (processors);
320 * There are two request/response formats: the basic request/response,
321 * possibly with one or more unsolicited informational responses (such
322 * as the WebDAV "102 Processing" response):
325 * W:HEADERS / R:NOT_STARTED -> R:HEADERS / W:NOT_STARTED
326 * W:BODY / R:NOT_STARTED -> R:BODY / W:NOT_STARTED
327 * [W:DONE / R:HEADERS (1xx) <- R:DONE / W:HEADERS (1xx) ...]
328 * W:DONE / R:HEADERS <- R:DONE / W:HEADERS
329 * W:DONE / R:BODY <- R:DONE / W:BODY
330 * W:DONE / R:DONE R:DONE / W:DONE
332 * and the "Expect: 100-continue" request/response, with the client
333 * blocking halfway through its request, and then either continuing or
334 * aborting, depending on the server response:
337 * W:HEADERS / R:NOT_STARTED -> R:HEADERS / W:NOT_STARTED
338 * W:BLOCKING / R:HEADERS <- R:BLOCKING / W:HEADERS
339 * [W:BODY / R:BLOCKING -> R:BODY / W:BLOCKING]
340 * [W:DONE / R:HEADERS <- R:DONE / W:HEADERS]
341 * W:DONE / R:BODY <- R:DONE / W:BODY
342 * W:DONE / R:DONE R:DONE / W:DONE
345 /* Attempts to push forward the writing side of @msg's I/O. Returns
346 * %TRUE if it manages to make some progress, and it is likely that
347 * further progress can be made. Returns %FALSE if it has reached a
348 * stopping point of some sort (need input from the application,
349 * socket not writable, write is complete, etc).
352 io_write (SoupMessage *msg, gboolean blocking,
353 GCancellable *cancellable, GError **error)
355 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
356 SoupMessageIOData *io = priv->io_data;
359 #if ENABLE(TIZEN_DLOG)
363 switch (io->write_state) {
364 case SOUP_MESSAGE_IO_STATE_HEADERS:
365 if (!io->write_buf->len) {
366 io->get_headers_cb (msg, io->write_buf,
371 #if ENABLE(TIZEN_DLOG)
372 uri = soup_uri_to_string(soup_message_get_uri(msg), FALSE);
373 TIZEN_LOGI("Request URL: %s", uri);
374 #if ENABLE(TIZEN_PERFORMANCE_TEST_LOG)
375 prctl_with_url("[BGN] soup_io_w : ", uri);
380 while (io->written < io->write_buf->len) {
381 nwrote = g_pollable_stream_write (io->ostream,
382 io->write_buf->str + io->written,
383 io->write_buf->len - io->written,
388 io->written += nwrote;
392 g_string_truncate (io->write_buf, 0);
394 if (io->mode == SOUP_MESSAGE_IO_SERVER &&
395 SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
396 if (msg->status_code == SOUP_STATUS_CONTINUE) {
397 /* Stop and wait for the body now */
399 SOUP_MESSAGE_IO_STATE_BLOCKING;
400 io->read_state = SOUP_MESSAGE_IO_STATE_BODY_START;
402 /* We just wrote a 1xx response
403 * header, so stay in STATE_HEADERS.
404 * (The caller will pause us from the
405 * wrote_informational callback if he
406 * is not ready to send the final
411 soup_message_wrote_informational (msg);
412 soup_message_cleanup_response (msg);
416 if (io->write_encoding == SOUP_ENCODING_CONTENT_LENGTH) {
417 SoupMessageHeaders *hdrs =
418 (io->mode == SOUP_MESSAGE_IO_CLIENT) ?
419 msg->request_headers : msg->response_headers;
420 io->write_length = soup_message_headers_get_content_length (hdrs);
423 if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
424 soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) {
425 /* Need to wait for the Continue response */
426 io->write_state = SOUP_MESSAGE_IO_STATE_BLOCKING;
427 io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
429 io->write_state = SOUP_MESSAGE_IO_STATE_BODY_START;
431 /* If the client was waiting for a Continue
432 * but we sent something else, then they're
435 if (io->mode == SOUP_MESSAGE_IO_SERVER &&
436 io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING)
437 io->read_state = SOUP_MESSAGE_IO_STATE_DONE;
440 soup_message_wrote_headers (msg);
444 case SOUP_MESSAGE_IO_STATE_BODY_START:
445 io->body_ostream = soup_body_output_stream_new (io->ostream,
448 io->write_state = SOUP_MESSAGE_IO_STATE_BODY;
452 case SOUP_MESSAGE_IO_STATE_BODY:
453 if (!io->write_length &&
454 io->write_encoding != SOUP_ENCODING_EOF &&
455 io->write_encoding != SOUP_ENCODING_CHUNKED) {
456 io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DONE;
460 if (!io->write_chunk) {
461 io->write_chunk = soup_message_body_get_chunk (io->write_body, io->write_body_offset);
462 if (!io->write_chunk) {
463 g_return_val_if_fail (!io->item || !io->item->new_api, FALSE);
464 soup_message_io_pause (msg);
467 if (!io->write_chunk->length) {
468 io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DONE;
473 nwrote = g_pollable_stream_write (io->body_ostream,
474 io->write_chunk->data + io->written,
475 io->write_chunk->length - io->written,
481 chunk = soup_buffer_new_subbuffer (io->write_chunk,
482 io->written, nwrote);
483 io->written += nwrote;
484 if (io->write_length)
485 io->write_length -= nwrote;
487 if (io->written == io->write_chunk->length)
488 io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DATA;
490 soup_message_wrote_body_data (msg, chunk);
491 soup_buffer_free (chunk);
495 case SOUP_MESSAGE_IO_STATE_BODY_DATA:
497 if (io->write_chunk->length == 0) {
498 io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DONE;
502 if (io->mode == SOUP_MESSAGE_IO_SERVER ||
503 priv->msg_flags & SOUP_MESSAGE_CAN_REBUILD)
504 soup_message_body_wrote_chunk (io->write_body, io->write_chunk);
505 io->write_body_offset += io->write_chunk->length;
506 soup_buffer_free (io->write_chunk);
507 io->write_chunk = NULL;
509 io->write_state = SOUP_MESSAGE_IO_STATE_BODY;
510 soup_message_wrote_chunk (msg);
514 case SOUP_MESSAGE_IO_STATE_BODY_DONE:
515 if (io->body_ostream) {
516 if (!g_output_stream_close (io->body_ostream, cancellable, error))
518 g_clear_object (&io->body_ostream);
521 io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
522 soup_message_wrote_body (msg);
526 case SOUP_MESSAGE_IO_STATE_FINISHING:
527 io->write_state = SOUP_MESSAGE_IO_STATE_DONE;
529 if (io->mode == SOUP_MESSAGE_IO_CLIENT)
530 io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
535 g_return_val_if_reached (FALSE);
541 /* Attempts to push forward the reading side of @msg's I/O. Returns
542 * %TRUE if it manages to make some progress, and it is likely that
543 * further progress can be made. Returns %FALSE if it has reached a
544 * stopping point of some sort (need input from the application,
545 * socket not readable, read is complete, etc).
548 io_read (SoupMessage *msg, gboolean blocking,
549 GCancellable *cancellable, GError **error)
551 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
552 SoupMessageIOData *io = priv->io_data;
553 guchar *stack_buf = NULL;
557 #if ENABLE(TIZEN_DLOG)
561 switch (io->read_state) {
562 case SOUP_MESSAGE_IO_STATE_HEADERS:
563 if (!read_headers (msg, blocking, cancellable, error))
566 status = io->parse_headers_cb (msg, (char *)io->read_header_buf->data,
567 io->read_header_buf->len,
569 io->header_data, error);
570 g_byte_array_set_size (io->read_header_buf, 0);
572 #if ENABLE(TIZEN_DLOG)
573 uri = soup_uri_to_string(soup_message_get_uri(msg), FALSE);
574 TIZEN_LOGI("Response URL: %s", uri);
575 #if ENABLE(TIZEN_PERFORMANCE_TEST_LOG)
576 prctl_with_url("[BGN] soup_io_r_hdr : ", uri);
581 if (status != SOUP_STATUS_OK) {
582 /* Either we couldn't parse the headers, or they
583 * indicated something that would mean we wouldn't
584 * be able to parse the body. (Eg, unknown
585 * Transfer-Encoding.). Skip the rest of the
586 * reading, and make sure the connection gets
587 * closed when we're done.
589 soup_message_set_status (msg, status);
590 soup_message_headers_append (msg->request_headers,
591 "Connection", "close");
592 io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
593 #if ENABLE(TIZEN_PERFORMANCE_TEST_LOG)
594 prctl_with_url_and_free("[END] soup_io_r_hdr,not ok : ",soup_uri_to_string(soup_message_get_uri(msg), FALSE));
599 if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
600 SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
601 if (msg->status_code == SOUP_STATUS_CONTINUE &&
602 io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING) {
603 /* Pause the reader, unpause the writer */
605 SOUP_MESSAGE_IO_STATE_BLOCKING;
607 SOUP_MESSAGE_IO_STATE_BODY_START;
609 /* Just stay in HEADERS */
610 io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
613 /* Informational responses have no bodies, so
614 * bail out here rather than parsing encoding, etc
616 soup_message_got_informational (msg);
617 soup_message_cleanup_response (msg);
619 } else if (io->mode == SOUP_MESSAGE_IO_SERVER &&
620 soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) {
621 /* The client requested a Continue response. The
622 * got_headers handler may change this to something
625 soup_message_set_status (msg, SOUP_STATUS_CONTINUE);
626 io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
627 io->read_state = SOUP_MESSAGE_IO_STATE_BLOCKING;
629 io->read_state = SOUP_MESSAGE_IO_STATE_BODY_START;
631 /* If the client was waiting for a Continue
632 * but got something else, then it's done
635 if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
636 io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING)
637 io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
640 if (io->read_encoding == SOUP_ENCODING_CONTENT_LENGTH) {
641 SoupMessageHeaders *hdrs =
642 (io->mode == SOUP_MESSAGE_IO_CLIENT) ?
643 msg->response_headers : msg->request_headers;
644 io->read_length = soup_message_headers_get_content_length (hdrs);
646 if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
647 !soup_message_is_keepalive (msg)) {
648 /* Some servers suck and send
649 * incorrect Content-Length values, so
650 * allow EOF termination in this case
651 * (iff the message is too short) too.
653 io->read_encoding = SOUP_ENCODING_EOF;
656 io->read_length = -1;
658 soup_message_got_headers (msg);
662 case SOUP_MESSAGE_IO_STATE_BODY_START:
663 if (!io->body_istream) {
664 GInputStream *body_istream = soup_body_input_stream_new (G_INPUT_STREAM (io->istream),
668 /* TODO: server-side messages do not have a io->item. This means
669 * that we cannot use content processors for them right now.
671 if (io->mode == SOUP_MESSAGE_IO_CLIENT) {
672 io->body_istream = soup_message_setup_body_istream (body_istream, msg,
674 SOUP_STAGE_MESSAGE_BODY);
675 g_object_unref (body_istream);
677 io->body_istream = body_istream;
682 SoupContentSnifferStream *sniffer_stream = SOUP_CONTENT_SNIFFER_STREAM (io->body_istream);
683 const char *content_type;
685 #if ENABLE (TIZEN_UPDATE_CACHE_ENTRY_CONTENT_TYPE_HEADER)
686 gboolean composite_type = FALSE;
689 if (!soup_content_sniffer_stream_is_ready (sniffer_stream, blocking,
693 content_type = soup_content_sniffer_stream_sniff (sniffer_stream, ¶ms);
694 soup_message_content_sniffed (msg, content_type, params);
695 #if ENABLE (TIZEN_UPDATE_CACHE_ENTRY_CONTENT_TYPE_HEADER)
696 if (content_type && msg->status_code != SOUP_STATUS_NOT_MODIFIED) {
701 g_hash_table_iter_init (&iter, params);
702 while (g_hash_table_iter_next (&iter, &key, &value)) {
703 composite_type = TRUE;
708 soup_cache_entry_set_content_type (io->item->session, msg, content_type);
713 io->read_state = SOUP_MESSAGE_IO_STATE_BODY;
717 case SOUP_MESSAGE_IO_STATE_BODY:
718 if (priv->chunk_allocator) {
719 buffer = priv->chunk_allocator (msg, io->read_length, priv->chunk_allocator_data);
721 g_return_val_if_fail (!io->item || !io->item->new_api, FALSE);
722 soup_message_io_pause (msg);
727 stack_buf = alloca (RESPONSE_BLOCK_SIZE);
728 buffer = soup_buffer_new (SOUP_MEMORY_TEMPORARY,
730 RESPONSE_BLOCK_SIZE);
733 nread = g_pollable_stream_read (io->body_istream,
734 (guchar *)buffer->data,
739 buffer->length = nread;
740 soup_message_body_got_chunk (io->read_body, buffer);
741 soup_message_got_chunk (msg, buffer);
742 soup_buffer_free (buffer);
746 soup_buffer_free (buffer);
750 /* else nread == 0 */
751 io->read_state = SOUP_MESSAGE_IO_STATE_BODY_DONE;
755 case SOUP_MESSAGE_IO_STATE_BODY_DONE:
756 io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
757 soup_message_got_body (msg);
761 case SOUP_MESSAGE_IO_STATE_FINISHING:
762 io->read_state = SOUP_MESSAGE_IO_STATE_DONE;
764 if (io->mode == SOUP_MESSAGE_IO_SERVER)
765 io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
770 g_return_val_if_reached (FALSE);
783 message_source_check (GSource *source)
785 SoupMessageSource *message_source = (SoupMessageSource *)source;
787 if (message_source->paused) {
788 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (message_source->msg);
789 SoupMessageIOData *io = priv->io_data;
791 if (!io || io->paused)
800 message_source_prepare (GSource *source,
804 return message_source_check (source);
808 message_source_dispatch (GSource *source,
809 GSourceFunc callback,
812 SoupMessageSourceFunc func = (SoupMessageSourceFunc)callback;
813 SoupMessageSource *message_source = (SoupMessageSource *)source;
815 return (*func) (message_source->msg, user_data);
819 message_source_finalize (GSource *source)
821 SoupMessageSource *message_source = (SoupMessageSource *)source;
823 g_object_unref (message_source->msg);
827 message_source_closure_callback (SoupMessage *msg,
830 GClosure *closure = data;
831 GValue param = G_VALUE_INIT;
832 GValue result_value = G_VALUE_INIT;
835 g_value_init (&result_value, G_TYPE_BOOLEAN);
837 g_value_init (¶m, SOUP_TYPE_MESSAGE);
838 g_value_set_object (¶m, msg);
840 g_closure_invoke (closure, &result_value, 1, ¶m, NULL);
842 result = g_value_get_boolean (&result_value);
843 g_value_unset (&result_value);
844 g_value_unset (¶m);
849 static GSourceFuncs message_source_funcs =
851 message_source_prepare,
852 message_source_check,
853 message_source_dispatch,
854 message_source_finalize,
855 (GSourceFunc)message_source_closure_callback,
856 (GSourceDummyMarshal)g_cclosure_marshal_generic,
860 soup_message_io_get_source (SoupMessage *msg, GCancellable *cancellable,
861 SoupMessageSourceFunc callback, gpointer user_data)
863 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
864 SoupMessageIOData *io = priv->io_data;
865 GSource *base_source, *source;
866 SoupMessageSource *message_source;
869 base_source = g_timeout_source_new (0);
870 } else if (io->paused) {
872 } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->read_state)) {
873 GPollableInputStream *istream;
875 if (io->body_istream)
876 istream = G_POLLABLE_INPUT_STREAM (io->body_istream);
878 istream = G_POLLABLE_INPUT_STREAM (io->istream);
879 base_source = g_pollable_input_stream_create_source (istream, cancellable);
880 } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->write_state)) {
881 GPollableOutputStream *ostream;
883 if (io->body_ostream)
884 ostream = G_POLLABLE_OUTPUT_STREAM (io->body_ostream);
886 ostream = G_POLLABLE_OUTPUT_STREAM (io->ostream);
887 base_source = g_pollable_output_stream_create_source (ostream, cancellable);
889 base_source = g_timeout_source_new (0);
891 source = g_source_new (&message_source_funcs,
892 sizeof (SoupMessageSource));
893 g_source_set_name (source, "SoupMessageSource");
894 message_source = (SoupMessageSource *)source;
895 message_source->msg = g_object_ref (msg);
896 message_source->paused = io && io->paused;
899 g_source_set_dummy_callback (base_source);
900 g_source_add_child_source (source, base_source);
901 g_source_unref (base_source);
903 g_source_set_callback (source, (GSourceFunc) callback, user_data, NULL);
908 request_is_restartable (SoupMessage *msg, GError *error)
910 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
911 SoupMessageIOData *io = priv->io_data;
916 return (io->mode == SOUP_MESSAGE_IO_CLIENT &&
917 io->read_state <= SOUP_MESSAGE_IO_STATE_HEADERS &&
918 io->read_header_buf->len == 0 &&
919 soup_connection_get_ever_used (io->item->conn) &&
920 !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT) &&
921 !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) &&
922 error->domain != G_TLS_ERROR &&
923 SOUP_METHOD_IS_IDEMPOTENT (msg->method));
927 io_run_until (SoupMessage *msg, gboolean blocking,
928 SoupMessageIOState read_state, SoupMessageIOState write_state,
929 GCancellable *cancellable, GError **error)
931 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
932 SoupMessageIOData *io = priv->io_data;
933 gboolean progress = TRUE, done;
934 GError *my_error = NULL;
936 if (g_cancellable_set_error_if_cancelled (cancellable, error))
939 g_set_error_literal (error, G_IO_ERROR,
940 G_IO_ERROR_CANCELLED,
941 _("Operation was cancelled"));
947 while (progress && priv->io_data == io && !io->paused &&
948 (io->read_state < read_state || io->write_state < write_state)) {
950 if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->read_state))
951 progress = io_read (msg, blocking, cancellable, &my_error);
952 else if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->write_state))
953 progress = io_write (msg, blocking, cancellable, &my_error);
959 if (request_is_restartable (msg, my_error)) {
960 /* Connection got closed, but we can safely try again */
961 g_error_free (my_error);
962 g_set_error_literal (error, SOUP_HTTP_ERROR,
963 SOUP_STATUS_TRY_AGAIN, "");
964 g_object_unref (msg);
968 g_propagate_error (error, my_error);
969 g_object_unref (msg);
971 } else if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
972 g_object_unref (msg);
974 } else if (priv->io_data != io) {
975 g_set_error_literal (error, G_IO_ERROR,
976 G_IO_ERROR_CANCELLED,
977 _("Operation was cancelled"));
978 g_object_unref (msg);
982 done = (io->read_state >= read_state &&
983 io->write_state >= write_state);
985 if (!blocking && !done) {
986 g_set_error_literal (error, G_IO_ERROR,
987 G_IO_ERROR_WOULD_BLOCK,
988 _("Operation would block"));
989 g_object_unref (msg);
993 g_object_unref (msg);
997 static void io_run (SoupMessage *msg, gboolean blocking);
1000 io_run_ready (SoupMessage *msg, gpointer user_data)
1002 io_run (msg, FALSE);
1007 io_run (SoupMessage *msg, gboolean blocking)
1009 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1010 SoupMessageIOData *io = priv->io_data;
1011 GError *error = NULL;
1012 GCancellable *cancellable;
1014 if (io->io_source) {
1015 g_source_destroy (io->io_source);
1016 g_source_unref (io->io_source);
1017 io->io_source = NULL;
1021 cancellable = io->cancellable ? g_object_ref (io->cancellable) : NULL;
1023 if (io_run_until (msg, blocking,
1024 SOUP_MESSAGE_IO_STATE_DONE,
1025 SOUP_MESSAGE_IO_STATE_DONE,
1026 cancellable, &error)) {
1027 soup_message_io_finished (msg);
1028 } else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
1029 g_clear_error (&error);
1030 io->io_source = soup_message_io_get_source (msg, NULL, io_run_ready, msg);
1031 g_source_attach (io->io_source, io->async_context);
1032 } else if (error && priv->io_data == io) {
1033 if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN))
1034 io->item->state = SOUP_MESSAGE_RESTARTING;
1035 else if (error->domain == G_TLS_ERROR) {
1036 soup_message_set_status_full (msg,
1037 SOUP_STATUS_SSL_FAILED,
1039 } else if (!SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code))
1040 soup_message_set_status (msg, SOUP_STATUS_IO_ERROR);
1042 g_error_free (error);
1043 soup_message_io_finished (msg);
1045 g_error_free (error);
1047 g_object_unref (msg);
1048 g_clear_object (&cancellable);
1052 soup_message_io_run_until_write (SoupMessage *msg, gboolean blocking,
1053 GCancellable *cancellable, GError **error)
1055 return io_run_until (msg, blocking,
1056 SOUP_MESSAGE_IO_STATE_ANY,
1057 SOUP_MESSAGE_IO_STATE_BODY,
1058 cancellable, error);
1062 soup_message_io_run_until_read (SoupMessage *msg, gboolean blocking,
1063 GCancellable *cancellable, GError **error)
1065 return io_run_until (msg, blocking,
1066 SOUP_MESSAGE_IO_STATE_BODY,
1067 SOUP_MESSAGE_IO_STATE_ANY,
1068 cancellable, error);
1072 soup_message_io_run_until_finish (SoupMessage *msg,
1074 GCancellable *cancellable,
1077 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1078 SoupMessageIOData *io = priv->io_data;
1084 g_return_val_if_fail (io->mode == SOUP_MESSAGE_IO_CLIENT, FALSE);
1086 if (io->read_state < SOUP_MESSAGE_IO_STATE_BODY_DONE)
1087 io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
1090 success = io_run_until (msg, blocking,
1091 SOUP_MESSAGE_IO_STATE_DONE,
1092 SOUP_MESSAGE_IO_STATE_DONE,
1093 cancellable, error);
1095 g_object_unref (msg);
1100 client_stream_eof (SoupClientInputStream *stream, gpointer user_data)
1102 SoupMessage *msg = user_data;
1103 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1104 SoupMessageIOData *io = priv->io_data;
1106 if (io && io->read_state == SOUP_MESSAGE_IO_STATE_BODY)
1107 io->read_state = SOUP_MESSAGE_IO_STATE_BODY_DONE;
1111 soup_message_io_get_response_istream (SoupMessage *msg,
1114 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1115 SoupMessageIOData *io = priv->io_data;
1116 GInputStream *client_stream;
1118 g_return_val_if_fail (io->mode == SOUP_MESSAGE_IO_CLIENT, NULL);
1120 if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
1121 g_set_error_literal (error, SOUP_HTTP_ERROR,
1122 msg->status_code, msg->reason_phrase);
1126 client_stream = soup_client_input_stream_new (io->body_istream, msg);
1127 g_signal_connect (client_stream, "eof",
1128 G_CALLBACK (client_stream_eof), msg);
1130 return client_stream;
1134 static SoupMessageIOData *
1135 new_iostate (SoupMessage *msg, GIOStream *iostream,
1136 GMainContext *async_context, SoupMessageIOMode mode,
1137 SoupMessageGetHeadersFn get_headers_cb,
1138 SoupMessageParseHeadersFn parse_headers_cb,
1139 gpointer header_data,
1140 SoupMessageCompletionFn completion_cb,
1141 gpointer completion_data)
1143 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1144 SoupMessageIOData *io;
1146 io = g_slice_new0 (SoupMessageIOData);
1148 io->get_headers_cb = get_headers_cb;
1149 io->parse_headers_cb = parse_headers_cb;
1150 io->header_data = header_data;
1151 io->completion_cb = completion_cb;
1152 io->completion_data = completion_data;
1154 io->iostream = g_object_ref (iostream);
1155 io->istream = SOUP_FILTER_INPUT_STREAM (g_io_stream_get_input_stream (iostream));
1156 io->ostream = g_io_stream_get_output_stream (iostream);
1159 io->async_context = g_main_context_ref (async_context);
1161 io->read_header_buf = g_byte_array_new ();
1162 io->write_buf = g_string_new (NULL);
1164 io->read_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
1165 io->write_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
1168 soup_message_io_cleanup (msg);
1174 soup_message_io_client (SoupMessageQueueItem *item,
1175 GIOStream *iostream,
1176 GMainContext *async_context,
1177 SoupMessageGetHeadersFn get_headers_cb,
1178 SoupMessageParseHeadersFn parse_headers_cb,
1179 gpointer header_data,
1180 SoupMessageCompletionFn completion_cb,
1181 gpointer completion_data)
1183 SoupMessageIOData *io;
1185 io = new_iostate (item->msg, iostream, async_context,
1186 SOUP_MESSAGE_IO_CLIENT,
1187 get_headers_cb, parse_headers_cb, header_data,
1188 completion_cb, completion_data);
1191 soup_message_queue_item_ref (item);
1192 io->cancellable = item->cancellable;
1194 io->read_body = item->msg->response_body;
1195 io->write_body = item->msg->request_body;
1197 io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
1199 if (!item->new_api) {
1201 SOUP_IS_SESSION_SYNC (item->session) ||
1202 (!SOUP_IS_SESSION_ASYNC (item->session) && !item->async);
1203 io_run (item->msg, blocking);
1208 soup_message_io_server (SoupMessage *msg,
1209 GIOStream *iostream, GMainContext *async_context,
1210 SoupMessageGetHeadersFn get_headers_cb,
1211 SoupMessageParseHeadersFn parse_headers_cb,
1212 gpointer header_data,
1213 SoupMessageCompletionFn completion_cb,
1214 gpointer completion_data)
1216 SoupMessageIOData *io;
1218 io = new_iostate (msg, iostream, async_context,
1219 SOUP_MESSAGE_IO_SERVER,
1220 get_headers_cb, parse_headers_cb, header_data,
1221 completion_cb, completion_data);
1223 io->read_body = msg->request_body;
1224 io->write_body = msg->response_body;
1226 io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
1227 io_run (msg, FALSE);
1231 soup_message_io_pause (SoupMessage *msg)
1233 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1234 SoupMessageIOData *io = priv->io_data;
1236 g_return_if_fail (io != NULL);
1238 if (io->item && io->item->new_api)
1239 g_return_if_fail (io->read_state < SOUP_MESSAGE_IO_STATE_BODY);
1241 if (io->io_source) {
1242 g_source_destroy (io->io_source);
1243 g_source_unref (io->io_source);
1244 io->io_source = NULL;
1247 if (io->unpause_source) {
1248 g_source_destroy (io->unpause_source);
1249 g_source_unref (io->unpause_source);
1250 io->unpause_source = NULL;
1257 io_unpause_internal (gpointer msg)
1259 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1260 SoupMessageIOData *io = priv->io_data;
1262 g_return_val_if_fail (io != NULL, FALSE);
1264 g_clear_pointer (&io->unpause_source, g_source_unref);
1270 io_run (msg, FALSE);
1275 soup_message_io_unpause (SoupMessage *msg)
1277 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1278 SoupMessageIOData *io = priv->io_data;
1280 g_return_if_fail (io != NULL);
1282 if (io->item && io->item->new_api) {
1283 g_return_if_fail (io->read_state < SOUP_MESSAGE_IO_STATE_BODY);
1288 if (!io->unpause_source) {
1289 io->unpause_source = soup_add_completion_reffed (io->async_context,
1290 io_unpause_internal, msg);
1295 * soup_message_io_in_progress:
1296 * @msg: a #SoupMessage
1298 * Tests whether or not I/O is currently in progress on @msg.
1300 * Return value: whether or not I/O is currently in progress.
1303 soup_message_io_in_progress (SoupMessage *msg)
1305 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
1307 return priv->io_data != NULL;