summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garcia Campos <cgarcia@igalia.com>2022-12-14 12:18:54 +0100
committerCarlos Garcia Campos <cgarcia@igalia.com>2022-12-14 12:18:54 +0100
commita35dc0a3a838f58042cb0dbdb1c57846af5f252f (patch)
treea1fbee3a7d7a4adfcbffffa0f6745d80ecf75b61
parent6308303e29bbd09966e431cde2df55b879b8df92 (diff)
downloadlibsoup-a35dc0a3a838f58042cb0dbdb1c57846af5f252f.tar.gz
session: add soup_session_send_and_splice() and soup_session_send_and_splice_async()
This is convenient API for reading the request body stream and writing it to an output stream.
-rw-r--r--examples/get.c70
-rw-r--r--libsoup/soup-session.c258
-rw-r--r--libsoup/soup-session.h23
3 files changed, 256 insertions, 95 deletions
diff --git a/examples/get.c b/examples/get.c
index 02c86f72..a2c97a3e 100644
--- a/examples/get.c
+++ b/examples/get.c
@@ -28,17 +28,13 @@ static const gchar *input_file_path;
#define OUTPUT_BUFFER_SIZE 8192
static void
-on_stream_splice (GObject *source, GAsyncResult *result, gpointer user_data)
+on_request_spliced (GObject *source, GAsyncResult *result, gpointer user_data)
{
GError *error = NULL;
- g_output_stream_splice_finish (G_OUTPUT_STREAM (source),
- result,
- &error);
- if (error) {
- g_printerr ("Failed to download: %s\n", error->message);
+
+ if (soup_session_send_and_splice_finish (SOUP_SESSION (source), result, &error) == -1) {
+ g_printerr ("Failed to send request: %s\n", error->message);
g_error_free (error);
- g_main_loop_quit (loop);
- return;
}
g_main_loop_quit (loop);
@@ -76,6 +72,7 @@ on_read_ready (GObject *source, GAsyncResult *result, gpointer user_data)
static void
on_request_sent (GObject *source, GAsyncResult *result, gpointer user_data)
{
+ char *output_buffer;
GError *error = NULL;
GInputStream *in = soup_session_send_finish (SOUP_SESSION (source), result, &error);
@@ -86,34 +83,9 @@ on_request_sent (GObject *source, GAsyncResult *result, gpointer user_data)
return;
}
- if (output_file_path) {
- GFile *output_file = g_file_new_for_commandline_arg (output_file_path);
- GOutputStream *out = G_OUTPUT_STREAM (g_file_create (output_file, G_FILE_CREATE_NONE,
- NULL, &error));
- if (error) {
- g_print ("Failed to create \"%s\": %s\n", output_file_path, error->message);
- g_error_free (error);
- g_object_unref (in);
- g_object_unref (output_file);
- g_main_loop_quit (loop);
- return;
- }
-
- /* Start downloading to the file */
- g_output_stream_splice_async (G_OUTPUT_STREAM (out), in,
- G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
- G_PRIORITY_DEFAULT,
- NULL,
- on_stream_splice,
- NULL);
-
- g_object_unref (out);
- } else {
- char *output_buffer = g_new (char, OUTPUT_BUFFER_SIZE);
- g_input_stream_read_all_async (in, output_buffer, OUTPUT_BUFFER_SIZE,
- G_PRIORITY_DEFAULT, NULL, on_read_ready, output_buffer);
- }
-
+ output_buffer = g_new (char, OUTPUT_BUFFER_SIZE);
+ g_input_stream_read_all_async (in, output_buffer, OUTPUT_BUFFER_SIZE,
+ G_PRIORITY_DEFAULT, NULL, on_read_ready, output_buffer);
g_object_unref (in);
}
@@ -334,8 +306,30 @@ main (int argc, char **argv)
/* Send the request */
soup_message_set_tls_client_certificate (msg, client_cert);
- soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, NULL,
- on_request_sent, NULL);
+ if (output_file_path) {
+ GFile *output_file = g_file_new_for_commandline_arg (output_file_path);
+ GOutputStream *out = G_OUTPUT_STREAM (g_file_create (output_file, G_FILE_CREATE_NONE,
+ NULL, &error));
+
+ if (error) {
+ g_print ("Failed to create \"%s\": %s\n", output_file_path, error->message);
+ g_error_free (error);
+ g_object_unref (output_file);
+ g_object_unref (msg);
+ g_object_unref (session);
+ exit (1);
+ }
+ soup_session_send_and_splice_async (session, msg, out,
+ G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
+ G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+ G_PRIORITY_DEFAULT,
+ NULL, on_request_spliced, NULL);
+ g_object_unref (output_file);
+ g_object_unref (out);
+ } else {
+ soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, NULL,
+ on_request_sent, NULL);
+ }
g_object_unref (msg);
/* Run the loop */
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 8b668036..cbc00b9d 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -3272,54 +3272,28 @@ soup_session_send (SoupSession *session,
}
static void
-send_and_read_splice_ready_cb (GOutputStream *ostream,
- GAsyncResult *result,
- GTask *task)
-{
- GError *error = NULL;
-
- if (g_output_stream_splice_finish (ostream, result, &error) != -1) {
- g_task_return_pointer (task,
- g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (ostream)),
- (GDestroyNotify)g_bytes_unref);
- } else {
- g_task_return_error (task, error);
- }
- g_object_unref (task);
-}
-
-static void
-send_and_read_stream_ready_cb (SoupSession *session,
+send_and_read_splice_ready_cb (SoupSession *session,
GAsyncResult *result,
GTask *task)
{
- GInputStream *stream;
GOutputStream *ostream;
GError *error = NULL;
+ ostream = g_task_get_task_data (task);
+
// In order for soup_session_get_async_result_message() to work it must
// have the task data for the task it wrapped
SoupMessageQueueItem *item = g_task_get_task_data (G_TASK (result));
g_task_set_task_data (task, soup_message_queue_item_ref (item), (GDestroyNotify)soup_message_queue_item_unref);
- stream = soup_session_send_finish (session, result, &error);
- if (!stream) {
- g_task_return_error (task, error);
- g_object_unref (task);
- return;
- }
-
- ostream = g_memory_output_stream_new_resizable ();
- g_output_stream_splice_async (ostream,
- stream,
- G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
- G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
- g_task_get_priority (task),
- g_task_get_cancellable (task),
- (GAsyncReadyCallback)send_and_read_splice_ready_cb,
- task);
- g_object_unref (ostream);
- g_object_unref (stream);
+ if (soup_session_send_and_splice_finish (session, result, &error) != -1) {
+ g_task_return_pointer (task,
+ g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (ostream)),
+ (GDestroyNotify)g_bytes_unref);
+ } else {
+ g_task_return_error (task, error);
+ }
+ g_object_unref (task);
}
/**
@@ -3350,18 +3324,23 @@ soup_session_send_and_read_async (SoupSession *session,
gpointer user_data)
{
GTask *task;
+ GOutputStream *ostream;
g_return_if_fail (SOUP_IS_SESSION (session));
g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ ostream = g_memory_output_stream_new_resizable ();
task = g_task_new (session, cancellable, callback, user_data);
g_task_set_priority (task, io_priority);
+ g_task_set_task_data (task, ostream, g_object_unref);
- soup_session_send_async (session, msg,
- g_task_get_priority (task),
- g_task_get_cancellable (task),
- (GAsyncReadyCallback)send_and_read_stream_ready_cb,
- task);
+ soup_session_send_and_splice_async (session, msg, ostream,
+ G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
+ G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+ g_task_get_priority (task),
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)send_and_read_splice_ready_cb,
+ task);
}
/**
@@ -3410,28 +3389,193 @@ soup_session_send_and_read (SoupSession *session,
GCancellable *cancellable,
GError **error)
{
- GInputStream *stream;
GOutputStream *ostream;
GBytes *bytes = NULL;
- stream = soup_session_send (session, msg, cancellable, error);
- if (!stream)
- return NULL;
-
- ostream = g_memory_output_stream_new_resizable ();
- if (g_output_stream_splice (ostream,
- stream,
- G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
- G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
- cancellable, error) != -1) {
- bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (ostream));
- }
- g_object_unref (ostream);
- g_object_unref (stream);
+ ostream = g_memory_output_stream_new_resizable ();
+ if (soup_session_send_and_splice (session, msg, ostream,
+ G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
+ G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+ cancellable, error) != -1)
+ bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (ostream));
+ g_object_unref (ostream);
return bytes;
}
+typedef struct {
+ GOutputStream *out_stream;
+ GOutputStreamSpliceFlags flags;
+ GTask *task;
+} SendAndSpliceAsyncData;
+
+static void
+send_and_splice_async_data_free (SendAndSpliceAsyncData *data)
+{
+ g_clear_object (&data->out_stream);
+ g_clear_object (&data->task);
+
+ g_free (data);
+}
+
+static void
+send_and_splice_ready_cb (GOutputStream *ostream,
+ GAsyncResult *result,
+ GTask *task)
+{
+ GError *error = NULL;
+ gssize retval;
+
+ retval = g_output_stream_splice_finish (ostream, result, &error);
+ if (retval != -1)
+ g_task_return_int (task, retval);
+ else
+ g_task_return_error (task, error);
+ g_object_unref (task);
+}
+
+static void
+send_and_splice_stream_ready_cb (SoupSession *session,
+ GAsyncResult *result,
+ SendAndSpliceAsyncData *data)
+{
+ GInputStream *stream;
+ GTask *task;
+ GError *error = NULL;
+
+ // In order for soup_session_get_async_result_message() to work it must
+ // have the task data for the task it wrapped
+ SoupMessageQueueItem *item = g_task_get_task_data (G_TASK (result));
+ g_task_set_task_data (data->task, soup_message_queue_item_ref (item), (GDestroyNotify)soup_message_queue_item_unref);
+
+ stream = soup_session_send_finish (session, result, &error);
+ if (!stream) {
+ g_task_return_error (data->task, error);
+ send_and_splice_async_data_free (data);
+ return;
+ }
+
+ task = g_steal_pointer (&data->task);
+ g_output_stream_splice_async (data->out_stream, stream, data->flags,
+ g_task_get_priority (task),
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)send_and_splice_ready_cb,
+ task);
+ g_object_unref (stream);
+ send_and_splice_async_data_free (data);
+}
+
+/**
+ * soup_session_send_and_splice_async:
+ * @session: a #SoupSession
+ * @msg: (transfer none): a #SoupMessage
+ * @out_stream: (transfer none): a #GOutputStream
+ * @flags: a set of #GOutputStreamSpliceFlags
+ * @io_priority: the I/O priority of the request
+ * @cancellable: (nullable): a #GCancellable
+ * @callback: (scope async): the callback to invoke
+ * @user_data: data for @callback
+ *
+ * Asynchronously sends @msg and splices the response body stream into @out_stream.
+ * When @callback is called, then either @msg has been sent and its response body
+ * spliced, or else an error has occurred.
+ *
+ * See [method@Session.send] for more details on the general semantics.
+ *
+ * Since: 3.4
+ */
+void
+soup_session_send_and_splice_async (SoupSession *session,
+ SoupMessage *msg,
+ GOutputStream *out_stream,
+ GOutputStreamSpliceFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SendAndSpliceAsyncData *data;
+
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (G_IS_OUTPUT_STREAM (out_stream));
+
+ data = g_new (SendAndSpliceAsyncData, 1);
+ data->out_stream = g_object_ref (out_stream);
+ data->flags = flags;
+ data->task = g_task_new (session, cancellable, callback, user_data);
+ g_task_set_priority (data->task, io_priority);
+
+ soup_session_send_async (session, msg,
+ g_task_get_priority (data->task),
+ g_task_get_cancellable (data->task),
+ (GAsyncReadyCallback)send_and_splice_stream_ready_cb,
+ data);
+}
+
+/**
+ * soup_session_send_and_splice_finish:
+ * @session: a #SoupSession
+ * @result: the #GAsyncResult passed to your callback
+ * @error: return location for a #GError, or %NULL
+ *
+ * Gets the response to a [method@Session.send_and_splice_async].
+ *
+ * Returns: a #gssize containing the size of the data spliced, or -1 if an error occurred.
+ *
+ * Since: 3.4
+ */
+gssize
+soup_session_send_and_splice_finish (SoupSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (SOUP_IS_SESSION (session), -1);
+ g_return_val_if_fail (g_task_is_valid (result, session), -1);
+
+ return g_task_propagate_int (G_TASK (result), error);
+}
+
+/**
+ * soup_session_send_and_splice:
+ * @session: a #SoupSession
+ * @msg: (transfer none): a #SoupMessage
+ * @out_stream: (transfer none): a #GOutputStream
+ * @flags: a set of #GOutputStreamSpliceFlags
+ * @cancellable: (nullable): a #GCancellable
+ * @error: return location for a #GError, or %NULL
+ *
+ * Synchronously sends @msg and splices the response body stream into @out_stream.
+ *
+ * See [method@Session.send] for more details on the general semantics.
+ *
+ * Returns: a #gssize containing the size of the data spliced, or -1 if an error occurred.
+ *
+ * Since: 3.4
+ */
+gssize
+soup_session_send_and_splice (SoupSession *session,
+ SoupMessage *msg,
+ GOutputStream *out_stream,
+ GOutputStreamSpliceFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GInputStream *stream;
+ gssize retval;
+
+ g_return_val_if_fail (G_IS_OUTPUT_STREAM (out_stream), -1);
+
+ stream = soup_session_send (session, msg, cancellable, error);
+ if (!stream)
+ return -1;
+
+ retval = g_output_stream_splice (out_stream, stream, flags, cancellable, error);
+ g_object_unref (stream);
+
+ return retval;
+}
+
/**
* soup_session_get_async_result_message:
* @session: a #SoupSession
diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h
index 13e5a4f5..215efcf2 100644
--- a/libsoup/soup-session.h
+++ b/libsoup/soup-session.h
@@ -166,6 +166,29 @@ GBytes *soup_session_send_and_read (SoupSession *session
GCancellable *cancellable,
GError **error);
+SOUP_AVAILABLE_IN_3_4
+void soup_session_send_and_splice_async(SoupSession *session,
+ SoupMessage *msg,
+ GOutputStream *out_stream,
+ GOutputStreamSpliceFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+SOUP_AVAILABLE_IN_3_4
+gssize soup_session_send_and_splice_finish(SoupSession *session,
+ GAsyncResult *result,
+ GError **error);
+
+SOUP_AVAILABLE_IN_3_4
+gssize soup_session_send_and_splice (SoupSession *session,
+ SoupMessage *msg,
+ GOutputStream *out_stream,
+ GOutputStreamSpliceFlags flags,
+ GCancellable *cancellable,
+ GError **error);
+
SOUP_AVAILABLE_IN_ALL
SoupMessage *soup_session_get_async_result_message (SoupSession *session,
GAsyncResult *result);