static gssize soup_content_sniffer_stream_skip (GInputStream *stream, gsize count, GCancellable *cancellable, GError **error) { SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream); gssize nskipped; if (sniffer->priv->sniffing) { /* Read into the internal buffer... */ nskipped = soup_content_sniffer_stream_read (stream, NULL, 0, cancellable, error); if (nskipped == -1) return -1; /* Now fall through */ } if (sniffer->priv->buffer) { nskipped = MIN (count, sniffer->priv->buffer_nread); if (nskipped == sniffer->priv->buffer_nread) { g_free (sniffer->priv->buffer); sniffer->priv->buffer = NULL; } else { /* FIXME */ memmove (sniffer->priv->buffer, sniffer->priv->buffer + nskipped, sniffer->priv->buffer_nread - nskipped); sniffer->priv->buffer_nread -= nskipped; } } else { nskipped = G_INPUT_STREAM_CLASS (soup_content_sniffer_stream_parent_class)-> skip (stream, count, cancellable, error); } return nskipped; }
static gboolean soup_content_sniffer_stream_is_readable (GPollableInputStream *stream) { SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream); if (sniffer->priv->error || (!sniffer->priv->sniffing && sniffer->priv->buffer)) return TRUE; return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (stream)->base_stream)); }
static void soup_content_sniffer_stream_finalize (GObject *object) { SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object); g_clear_object (&sniffer->priv->sniffer); g_clear_object (&sniffer->priv->msg); g_free (sniffer->priv->buffer); g_clear_error (&sniffer->priv->error); g_free (sniffer->priv->sniffed_type); g_clear_pointer (&sniffer->priv->sniffed_params, g_hash_table_unref); G_OBJECT_CLASS (soup_content_sniffer_stream_parent_class)->finalize (object); }
static void soup_content_sniffer_stream_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object); switch (prop_id) { case PROP_SNIFFER: g_value_set_object (value, sniffer->priv->sniffer); break; case PROP_MESSAGE: g_value_set_object (value, sniffer->priv->msg); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gssize read_and_sniff (GInputStream *stream, gboolean blocking, GCancellable *cancellable, GError **error) { SoupContentSnifferStreamPrivate *priv = SOUP_CONTENT_SNIFFER_STREAM (stream)->priv; gssize nread; GError *my_error = NULL; SoupBuffer *buf; do { nread = g_pollable_stream_read (G_FILTER_INPUT_STREAM (stream)->base_stream, priv->buffer + priv->buffer_nread, priv->buffer_size - priv->buffer_nread, blocking, cancellable, &my_error); if (nread <= 0) break; priv->buffer_nread += nread; } while (priv->buffer_nread < priv->buffer_size); /* If we got EAGAIN or cancellation before filling the buffer, * just return that right away. Likewise if we got any other * error without ever reading any data. Otherwise, save the * error to return after we're done sniffing. */ if (my_error) { if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) || g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || priv->buffer_nread == 0) { g_propagate_error (error, my_error); return -1; } else priv->error = my_error; } /* Sniff, then return the data */ buf = soup_buffer_new (SOUP_MEMORY_TEMPORARY, priv->buffer, priv->buffer_nread); priv->sniffed_type = soup_content_sniffer_sniff (priv->sniffer, priv->msg, buf, &priv->sniffed_params); soup_buffer_free (buf); priv->sniffing = FALSE; return priv->buffer_nread; }
static gssize read_internal (GInputStream *stream, void *buffer, gsize count, gboolean blocking, GCancellable *cancellable, GError **error) { SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream); gssize nread; if (sniffer->priv->error) { g_propagate_error (error, sniffer->priv->error); sniffer->priv->error = NULL; return -1; } if (sniffer->priv->sniffing) { nread = read_and_sniff (stream, blocking, cancellable, error); if (nread <= 0) return nread; } if (sniffer->priv->buffer) { nread = MIN (count, sniffer->priv->buffer_nread); memcpy (buffer, sniffer->priv->buffer, nread); if (nread == sniffer->priv->buffer_nread) { g_free (sniffer->priv->buffer); sniffer->priv->buffer = NULL; } else { /* FIXME, inefficient */ memmove (sniffer->priv->buffer, sniffer->priv->buffer + nread, sniffer->priv->buffer_nread - nread); sniffer->priv->buffer_nread -= nread; } } else { nread = g_pollable_stream_read (G_FILTER_INPUT_STREAM (stream)->base_stream, buffer, count, blocking, cancellable, error); } return nread; }
static GSource * soup_content_sniffer_stream_create_source (GPollableInputStream *stream, GCancellable *cancellable) { SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream); GSource *base_source, *pollable_source; if (sniffer->priv->error || (!sniffer->priv->sniffing && sniffer->priv->buffer)) base_source = g_timeout_source_new (0); else base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (stream)->base_stream), cancellable); g_source_set_dummy_callback (base_source); pollable_source = g_pollable_source_new (G_OBJECT (stream)); g_source_add_child_source (pollable_source, base_source); g_source_unref (base_source); return pollable_source; }
static void soup_content_sniffer_stream_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object); switch (prop_id) { case PROP_SNIFFER: sniffer->priv->sniffer = g_value_dup_object (value); /* FIXME: supposed to wait until after got-headers for this */ sniffer->priv->buffer_size = soup_content_sniffer_get_buffer_size (sniffer->priv->sniffer); sniffer->priv->buffer = g_malloc (sniffer->priv->buffer_size); break; case PROP_MESSAGE: sniffer->priv->msg = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
/* Attempts to push forward the reading side of @msg's I/O. Returns * %TRUE if it manages to make some progress, and it is likely that * further progress can be made. Returns %FALSE if it has reached a * stopping point of some sort (need input from the application, * socket not readable, read is complete, etc). */ static gboolean io_read (SoupMessage *msg, gboolean blocking, GCancellable *cancellable, GError **error) { SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg); SoupMessageIOData *io = priv->io_data; guchar *stack_buf = NULL; gssize nread; SoupBuffer *buffer; guint status; switch (io->read_state) { case SOUP_MESSAGE_IO_STATE_HEADERS: if (!read_headers (msg, blocking, cancellable, error)) return FALSE; status = io->parse_headers_cb (msg, (char *)io->read_header_buf->data, io->read_header_buf->len, &io->read_encoding, io->header_data, error); g_byte_array_set_size (io->read_header_buf, 0); if (status != SOUP_STATUS_OK) { /* Either we couldn't parse the headers, or they * indicated something that would mean we wouldn't * be able to parse the body. (Eg, unknown * Transfer-Encoding.). Skip the rest of the * reading, and make sure the connection gets * closed when we're done. */ soup_message_set_status (msg, status); soup_message_headers_append (msg->request_headers, "Connection", "close"); io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; break; } if (io->mode == SOUP_MESSAGE_IO_CLIENT && SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) { if (msg->status_code == SOUP_STATUS_CONTINUE && io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING) { /* Pause the reader, unpause the writer */ io->read_state = SOUP_MESSAGE_IO_STATE_BLOCKING; io->write_state = SOUP_MESSAGE_IO_STATE_BODY_START; } else { /* Just stay in HEADERS */ io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS; } /* Informational responses have no bodies, so * bail out here rather than parsing encoding, etc */ soup_message_got_informational (msg); /* If this was "101 Switching Protocols", then * the session may have stolen the connection... */ if (io != priv->io_data) return FALSE; soup_message_cleanup_response (msg); break; } else if (io->mode == SOUP_MESSAGE_IO_SERVER && soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) { /* We must return a status code and response * headers to the client; either an error to * be set by a got-headers handler below, or * else %SOUP_STATUS_CONTINUE otherwise. */ io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS; io->read_state = SOUP_MESSAGE_IO_STATE_BLOCKING; } else { io->read_state = SOUP_MESSAGE_IO_STATE_BODY_START; /* If the client was waiting for a Continue * but got something else, then it's done * writing. */ if (io->mode == SOUP_MESSAGE_IO_CLIENT && io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING) io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING; } if (io->read_encoding == SOUP_ENCODING_CONTENT_LENGTH) { SoupMessageHeaders *hdrs = (io->mode == SOUP_MESSAGE_IO_CLIENT) ? msg->response_headers : msg->request_headers; io->read_length = soup_message_headers_get_content_length (hdrs); if (io->mode == SOUP_MESSAGE_IO_CLIENT && !soup_message_is_keepalive (msg)) { /* Some servers suck and send * incorrect Content-Length values, so * allow EOF termination in this case * (iff the message is too short) too. */ io->read_encoding = SOUP_ENCODING_EOF; } } else io->read_length = -1; soup_message_got_headers (msg); break; case SOUP_MESSAGE_IO_STATE_BODY_START: if (!io->body_istream) { GInputStream *body_istream = soup_body_input_stream_new (G_INPUT_STREAM (io->istream), io->read_encoding, io->read_length); /* TODO: server-side messages do not have a io->item. This means * that we cannot use content processors for them right now. */ if (io->mode == SOUP_MESSAGE_IO_CLIENT) { io->body_istream = soup_message_setup_body_istream (body_istream, msg, io->item->session, SOUP_STAGE_MESSAGE_BODY); g_object_unref (body_istream); } else { io->body_istream = body_istream; } } if (priv->sniffer) { SoupContentSnifferStream *sniffer_stream = SOUP_CONTENT_SNIFFER_STREAM (io->body_istream); const char *content_type; GHashTable *params; if (!soup_content_sniffer_stream_is_ready (sniffer_stream, blocking, cancellable, error)) return FALSE; content_type = soup_content_sniffer_stream_sniff (sniffer_stream, ¶ms); soup_message_content_sniffed (msg, content_type, params); } io->read_state = SOUP_MESSAGE_IO_STATE_BODY; break; case SOUP_MESSAGE_IO_STATE_BODY: if (priv->chunk_allocator) { buffer = priv->chunk_allocator (msg, io->read_length, priv->chunk_allocator_data); if (!buffer) { g_return_val_if_fail (!io->item || !io->item->new_api, FALSE); soup_message_io_pause (msg); return FALSE; } } else { if (!stack_buf) stack_buf = alloca (RESPONSE_BLOCK_SIZE); buffer = soup_buffer_new (SOUP_MEMORY_TEMPORARY, stack_buf, RESPONSE_BLOCK_SIZE); } nread = g_pollable_stream_read (io->body_istream, (guchar *)buffer->data, buffer->length, blocking, cancellable, error); if (nread > 0) { buffer->length = nread; soup_message_body_got_chunk (io->read_body, buffer); soup_message_got_chunk (msg, buffer); soup_buffer_free (buffer); break; } soup_buffer_free (buffer); if (nread == -1) return FALSE; /* else nread == 0 */ io->read_state = SOUP_MESSAGE_IO_STATE_BODY_DONE; break; case SOUP_MESSAGE_IO_STATE_BODY_DONE: io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; soup_message_got_body (msg); break; case SOUP_MESSAGE_IO_STATE_FINISHING: io->read_state = SOUP_MESSAGE_IO_STATE_DONE; if (io->mode == SOUP_MESSAGE_IO_SERVER) io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS; break; default: g_return_val_if_reached (FALSE); } return TRUE; }