/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ /* * soup-message-io-data.c: HTTP message I/O data * * Copyright (C) 2000-2003, Ximian, Inc. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "soup-message-io-data.h" #include "soup-message-private.h" #include "soup-server-message-private.h" #include "soup.h" #define RESPONSE_BLOCK_SIZE 8192 #define HEADER_SIZE_LIMIT (64 * 1024) void soup_message_io_data_cleanup (SoupMessageIOData *io) { if (io->io_source) { g_source_destroy (io->io_source); g_source_unref (io->io_source); io->io_source = NULL; } if (io->body_istream) g_object_unref (io->body_istream); if (io->body_ostream) g_object_unref (io->body_ostream); g_byte_array_free (io->read_header_buf, TRUE); g_string_free (io->write_buf, TRUE); if (io->async_wait) { g_cancellable_cancel (io->async_wait); g_clear_object (&io->async_wait); } g_clear_error (&io->async_error); } gboolean soup_message_io_data_read_headers (SoupMessageIOData *io, SoupFilterInputStream *istream, gboolean blocking, GCancellable *cancellable, gushort *extra_bytes, GError **error) { gssize nread, old_len; gboolean got_lf; while (1) { old_len = io->read_header_buf->len; g_byte_array_set_size (io->read_header_buf, old_len + RESPONSE_BLOCK_SIZE); nread = soup_filter_input_stream_read_line (istream, io->read_header_buf->data + old_len, RESPONSE_BLOCK_SIZE, blocking, &got_lf, cancellable, error); io->read_header_buf->len = old_len + MAX (nread, 0); if (nread == 0) { if (io->read_header_buf->len > 0) { if (extra_bytes) *extra_bytes = 0; break; } g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, _("Connection terminated unexpectedly")); } if (nread <= 0) return FALSE; if (got_lf) { if (nread == 1 && old_len >= 2 && !strncmp ((char *)io->read_header_buf->data + io->read_header_buf->len - 2, "\n\n", 2)) { io->read_header_buf->len--; if (extra_bytes) *extra_bytes = 1; break; } else if (nread == 2 && old_len >= 3 && !strncmp ((char *)io->read_header_buf->data + io->read_header_buf->len - 3, "\n\r\n", 3)) { io->read_header_buf->len -= 2; if (extra_bytes) *extra_bytes = 2; break; } } if (io->read_header_buf->len > HEADER_SIZE_LIMIT) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, _("Header too big")); return FALSE; } } io->read_header_buf->data[io->read_header_buf->len] = '\0'; return TRUE; } static gboolean message_io_is_paused (GObject *msg) { if (SOUP_IS_MESSAGE (msg)) return soup_message_is_io_paused (SOUP_MESSAGE (msg)); if (SOUP_IS_SERVER_MESSAGE (msg)) return soup_server_message_is_io_paused (SOUP_SERVER_MESSAGE (msg)); return FALSE; } static gboolean message_io_source_check (GSource *source) { SoupMessageIOSource *message_source = (SoupMessageIOSource *)source; if (message_source->paused) { if (message_io_is_paused (message_source->msg)) return FALSE; return TRUE; } else return FALSE; } GSource * soup_message_io_data_get_source (SoupMessageIOData *io, GObject *msg, GInputStream *istream, GOutputStream *ostream, GCancellable *cancellable, SoupMessageIOSourceFunc callback, gpointer user_data) { GSource *base_source, *source; if (!io) { base_source = g_timeout_source_new (0); } else if (io->paused) { base_source = cancellable ? g_cancellable_source_new (cancellable) : NULL; } else if (io->async_wait) { base_source = g_cancellable_source_new (io->async_wait); } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->read_state)) { GPollableInputStream *stream; if (io->body_istream) stream = G_POLLABLE_INPUT_STREAM (io->body_istream); else if (istream) stream = G_POLLABLE_INPUT_STREAM (istream); else g_assert_not_reached (); base_source = g_pollable_input_stream_create_source (stream, cancellable); } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->write_state)) { GPollableOutputStream *stream; if (io->body_ostream) stream = G_POLLABLE_OUTPUT_STREAM (io->body_ostream); else if (ostream) stream = G_POLLABLE_OUTPUT_STREAM (ostream); else g_assert_not_reached (); base_source = g_pollable_output_stream_create_source (stream, cancellable); } else base_source = g_timeout_source_new (0); source = soup_message_io_source_new (base_source, msg, io && io->paused, message_io_source_check); g_source_set_callback (source, (GSourceFunc) callback, user_data, NULL); return source; } void soup_message_io_data_pause (SoupMessageIOData *io) { if (io->io_source) { g_source_destroy (io->io_source); g_source_unref (io->io_source); io->io_source = NULL; } io->paused = TRUE; } void soup_message_io_data_unpause (SoupMessageIOData *io) { io->paused = FALSE; }