diff options
author | Carlos Garcia Campos <cgarcia@igalia.com> | 2022-12-14 12:18:54 +0100 |
---|---|---|
committer | Carlos Garcia Campos <cgarcia@igalia.com> | 2022-12-14 12:18:54 +0100 |
commit | a35dc0a3a838f58042cb0dbdb1c57846af5f252f (patch) | |
tree | a1fbee3a7d7a4adfcbffffa0f6745d80ecf75b61 | |
parent | 6308303e29bbd09966e431cde2df55b879b8df92 (diff) | |
download | libsoup-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.c | 70 | ||||
-rw-r--r-- | libsoup/soup-session.c | 258 | ||||
-rw-r--r-- | libsoup/soup-session.h | 23 |
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); |