/*
* Copyright 2021 Igalia S.L.
*
* This file is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program. If not, see .
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "test-utils.h"
#include "soup-connection.h"
#include "soup-message-private.h"
#include "soup-message-headers-private.h"
#include "soup-server-message-private.h"
#include "soup-body-input-stream-http2.h"
#include
static GUri *base_uri;
typedef struct {
SoupSession *session;
} Test;
#define LARGE_N_CHARS 24
#define LARGE_CHARS_REPEAT 1024
static void
setup_session (Test *test, gconstpointer data)
{
test->session = soup_test_session_new (NULL);
}
static void
teardown_session (Test *test, gconstpointer data)
{
soup_test_session_abort_unref (test->session);
}
static void
do_basic_async_test (Test *test, gconstpointer data)
{
SoupMessage *msg;
GBytes *response;
GError *error = NULL;
msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri);
g_assert_cmpuint (soup_message_get_http_version (msg), ==, SOUP_HTTP_1_1);
response = soup_test_session_async_send (test->session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_cmpuint (soup_message_get_http_version (msg), ==, SOUP_HTTP_2_0);
g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Hello world");
g_bytes_unref (response);
g_object_unref (msg);
}
static void
do_basic_sync_test (Test *test, gconstpointer data)
{
SoupMessage *msg;
GBytes *response;
GError *error = NULL;
msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri);
g_assert_cmpuint (soup_message_get_http_version (msg), ==, SOUP_HTTP_1_1);
response = soup_session_send_and_read (test->session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_cmpuint (soup_message_get_http_version (msg), ==, SOUP_HTTP_2_0);
g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Hello world");
g_bytes_unref (response);
g_object_unref (msg);
}
static void
do_no_content_async_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GBytes *response;
GError *error = NULL;
uri = g_uri_parse_relative (base_uri, "/no-content", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
response = soup_test_session_async_send (test->session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_cmpuint (soup_message_get_status (msg), ==, 204);
g_assert_cmpuint (g_bytes_get_size (response), ==, 0);
g_uri_unref (uri);
g_bytes_unref (response);
g_object_unref (msg);
}
static void
do_large_test (Test *test, gconstpointer data)
{
gboolean async = GPOINTER_TO_INT (data);
GUri *uri;
SoupMessage *msg;
GBytes *response;
GError *error = NULL;
uri = g_uri_parse_relative (base_uri, "/large", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
/* This is both large and read in chunks */
if (async)
response = soup_test_session_async_send (test->session, msg, NULL, &error);
else
response = soup_session_send_and_read (test->session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_cmpuint (g_bytes_get_size (response), ==, (LARGE_N_CHARS * LARGE_CHARS_REPEAT) + 1);
g_uri_unref (uri);
g_bytes_unref (response);
g_object_unref (msg);
}
static GBytes *
read_stream_to_bytes_sync (GInputStream *stream)
{
GOutputStream *out = g_memory_output_stream_new_resizable ();
GError *error = NULL;
gssize read = g_output_stream_splice (out, stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
NULL, &error);
g_assert_no_error (error);
g_assert_cmpint (read, >=, 0);
GBytes *bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (out));
g_object_unref (out);
return bytes;
}
static void
on_send_complete (GObject *source, GAsyncResult *res, gpointer user_data)
{
SoupSession *sess = SOUP_SESSION (source);
GError *error = NULL;
GInputStream *stream;
GBytes **bytes_out = user_data;
stream = soup_session_send_finish (sess, res, &error);
g_assert_no_error (error);
g_assert_nonnull (stream);
*bytes_out = read_stream_to_bytes_sync (stream);
g_object_unref (stream);
}
static void
do_multi_message_async_test (Test *test, gconstpointer data)
{
GMainContext *async_context = g_main_context_ref_thread_default ();
GUri *uri1, *uri2;
SoupMessage *msg1, *msg2;
GBytes *response1 = NULL;
GBytes *response2 = NULL;
uri1 = g_uri_parse_relative (base_uri, "echo_query?body%201", SOUP_HTTP_URI_FLAGS, NULL);
msg1 = soup_message_new_from_uri (SOUP_METHOD_GET, uri1);
soup_message_set_http_version (msg1, SOUP_HTTP_2_0);
uri2 = g_uri_parse_relative (base_uri, "echo_query?body%202", SOUP_HTTP_URI_FLAGS, NULL);
msg2 = soup_message_new_from_uri (SOUP_METHOD_GET, uri2);
soup_message_set_http_version (msg2, SOUP_HTTP_2_0);
soup_session_send_async (test->session, msg1, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response1);
soup_session_send_async (test->session, msg2, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response2);
while (!response1 || !response2) {
g_main_context_iteration (async_context, TRUE);
}
g_assert_cmpuint (soup_message_get_http_version (msg1), ==, SOUP_HTTP_2_0);
g_assert_cmpuint (soup_message_get_http_version (msg2), ==, SOUP_HTTP_2_0);
g_assert_cmpstr (g_bytes_get_data (response1, NULL), ==, "body%201");
g_assert_cmpstr (g_bytes_get_data (response2, NULL), ==, "body%202");
while (g_main_context_pending (async_context))
g_main_context_iteration (async_context, FALSE);
g_bytes_unref (response1);
g_bytes_unref (response2);
g_object_unref (msg1);
g_object_unref (msg2);
g_uri_unref (uri1);
g_uri_unref (uri2);
g_main_context_unref (async_context);
}
static void
on_send_and_read_cancelled_complete (SoupSession *session,
GAsyncResult *result,
gboolean *done)
{
GError *error = NULL;
GBytes *response = soup_session_send_and_read_finish (session, result, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
g_assert_null (response);
g_error_free (error);
*done = TRUE;
}
static void
on_send_and_read_complete (SoupSession *session,
GAsyncResult *result,
gboolean *done)
{
GError *error = NULL;
GBytes *response = soup_session_send_and_read_finish (session, result, &error);
g_assert_no_error (error);
g_assert_nonnull (response);
g_bytes_unref (response);
*done = TRUE;
}
static void
do_cancellation_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GMainContext *async_context = g_main_context_ref_thread_default ();
GCancellable *cancellable = g_cancellable_new ();
gboolean done = FALSE;
uri = g_uri_parse_relative (base_uri, "/large", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
soup_session_send_and_read_async (test->session, msg, G_PRIORITY_DEFAULT, cancellable,
(GAsyncReadyCallback)on_send_and_read_cancelled_complete, &done);
/* Cancel right after getting the headers */
g_signal_connect_swapped (msg, "got-headers", G_CALLBACK (g_cancellable_cancel), cancellable);
while (!done)
g_main_context_iteration (async_context, FALSE);
g_object_unref (msg);
done = FALSE;
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
soup_session_send_and_read_async (test->session, msg, G_PRIORITY_DEFAULT, NULL,
(GAsyncReadyCallback)on_send_and_read_complete, &done);
while (!done)
g_main_context_iteration (async_context, FALSE);
g_uri_unref (uri);
g_object_unref (msg);
g_object_unref (cancellable);
g_main_context_unref (async_context);
}
static void
do_one_cancel_after_send_request_test (SoupSession *session,
gboolean reuse_cancellable,
gboolean cancelled_by_session)
{
SoupMessage *msg;
GCancellable *cancellable;
GInputStream *istream;
GOutputStream *ostream;
guint flags = SOUP_TEST_REQUEST_CANCEL_AFTER_SEND_FINISH;
GBytes *body;
GError *error = NULL;
if (cancelled_by_session)
flags |= SOUP_TEST_REQUEST_CANCEL_BY_SESSION;
msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri);
cancellable = g_cancellable_new ();
istream = soup_test_request_send (session, msg, cancellable, flags, &error);
g_assert_no_error (error);
g_assert_nonnull (istream);
/* If we use a new cancellable to read the stream
* it shouldn't fail with cancelled error.
*/
if (!reuse_cancellable) {
g_object_unref (cancellable);
cancellable = g_cancellable_new ();
}
ostream = g_memory_output_stream_new_resizable ();
g_output_stream_splice (ostream, istream,
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
cancellable, &error);
if (reuse_cancellable || cancelled_by_session) {
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
g_clear_error (&error);
} else {
g_assert_no_error (error);
body = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (ostream));
g_assert_cmpstr (g_bytes_get_data (body, NULL), ==, "Hello world");
g_bytes_unref (body);
}
while (g_main_context_pending (NULL))
g_main_context_iteration (NULL, FALSE);
g_object_unref (cancellable);
g_object_unref (ostream);
g_object_unref (istream);
g_object_unref (msg);
}
static void
do_cancellation_after_send_test (Test *test, gconstpointer data)
{
do_one_cancel_after_send_request_test (test->session, TRUE, FALSE);
do_one_cancel_after_send_request_test (test->session, FALSE, FALSE);
do_one_cancel_after_send_request_test (test->session, FALSE, TRUE);
}
static void
do_post_sync_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GInputStream *response;
GBytes *bytes = g_bytes_new_static ("body 1", sizeof ("body 1"));
GError *error = NULL;
uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri);
soup_message_set_request_body_from_bytes (msg, "text/plain", bytes);
response = soup_session_send (test->session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (response);
GBytes *response_bytes = read_stream_to_bytes_sync (response);
g_assert_cmpstr (g_bytes_get_data (response_bytes, NULL), ==, "body 1");
g_bytes_unref (response_bytes);
g_object_unref (response);
g_bytes_unref (bytes);
g_object_unref (msg);
g_uri_unref (uri);
}
static void
do_post_large_sync_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GInputStream *response;
guint large_size = 10000;
char *large_data;
unsigned int i;
GError *error = NULL;
large_data = g_malloc (large_size);
for (i = 0; i < large_size; i++)
large_data[i] = i & 0xFF;
GBytes *bytes = g_bytes_new_take (large_data, large_size);
uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri);
soup_message_set_request_body_from_bytes (msg, "text/plain", bytes);
response = soup_session_send (test->session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (response);
GBytes *response_bytes = read_stream_to_bytes_sync (response);
g_assert_true (g_bytes_equal (bytes, response_bytes));
g_bytes_unref (response_bytes);
g_object_unref (response);
g_bytes_unref (bytes);
g_object_unref (msg);
g_uri_unref (uri);
}
static void
do_post_async_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GBytes *response = NULL;
GMainContext *async_context = g_main_context_ref_thread_default ();
GBytes *bytes = g_bytes_new_static ("body 1", sizeof ("body 1"));
uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri);
soup_message_set_request_body_from_bytes (msg, "text/plain", bytes);
soup_session_send_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response);
while (!response)
g_main_context_iteration (async_context, TRUE);
g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "body 1");
while (g_main_context_pending (async_context))
g_main_context_iteration (async_context, FALSE);
g_bytes_unref (response);
g_bytes_unref (bytes);
g_main_context_unref (async_context);
g_object_unref (msg);
g_uri_unref (uri);
}
static void
do_post_large_async_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GBytes *response = NULL;
GMainContext *async_context = g_main_context_ref_thread_default ();
guint large_size = 10000;
char *large_data;
unsigned int i;
large_data = g_malloc (large_size);
for (i = 0; i < large_size; i++)
large_data[i] = i & 0xFF;
GBytes *bytes = g_bytes_new_take (large_data, large_size);
uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri);
soup_message_set_request_body_from_bytes (msg, "text/plain", bytes);
soup_session_send_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response);
while (!response)
g_main_context_iteration (async_context, TRUE);
g_assert_true (g_bytes_equal (bytes, response));
while (g_main_context_pending (async_context))
g_main_context_iteration (async_context, FALSE);
g_bytes_unref (response);
g_bytes_unref (bytes);
g_main_context_unref (async_context);
g_object_unref (msg);
g_uri_unref (uri);
}
static void
do_post_blocked_async_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GBytes *response = NULL;
GMainContext *async_context = g_main_context_ref_thread_default ();
GInputStream *in_stream = soup_body_input_stream_http2_new ();
soup_body_input_stream_http2_add_data (SOUP_BODY_INPUT_STREAM_HTTP2 (in_stream), (guint8*)"Part 1 -", 8);
uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri);
soup_message_set_request_body (msg, "text/plain", in_stream, 8 + 8);
soup_session_send_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response);
while (!response) {
// Let it iterate for a bit waiting on blocked data
if (soup_body_input_stream_http2_is_blocked (SOUP_BODY_INPUT_STREAM_HTTP2 (in_stream))) {
soup_body_input_stream_http2_add_data (SOUP_BODY_INPUT_STREAM_HTTP2 (in_stream), (guint8*)" Part 2", 8);
soup_body_input_stream_http2_complete (SOUP_BODY_INPUT_STREAM_HTTP2 (in_stream));
}
g_main_context_iteration (async_context, TRUE);
}
g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Part 1 - Part 2");
while (g_main_context_pending (async_context))
g_main_context_iteration (async_context, FALSE);
g_bytes_unref (response);
g_object_unref (in_stream);
g_main_context_unref (async_context);
g_object_unref (msg);
g_uri_unref (uri);
}
static void
do_post_file_async_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GBytes *response = NULL;
GMainContext *async_context = g_main_context_ref_thread_default ();
GFile *in_file = g_file_new_for_path (g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL));
GFileInputStream *in_stream = g_file_read (in_file, NULL, NULL);
g_assert_nonnull (in_stream);
uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri);
soup_message_set_request_body (msg, "application/x-x509-ca-cert", G_INPUT_STREAM (in_stream), -1);
soup_session_send_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response);
while (!response)
g_main_context_iteration (async_context, TRUE);
g_assert_true (g_str_has_prefix (g_bytes_get_data (response, NULL), "-----BEGIN CERTIFICATE-----"));
while (g_main_context_pending (async_context))
g_main_context_iteration (async_context, FALSE);
g_bytes_unref (response);
g_object_unref (in_stream);
g_object_unref (in_file);
g_main_context_unref (async_context);
g_object_unref (msg);
g_uri_unref (uri);
}
static gboolean
on_delayed_auth (SoupAuth *auth)
{
g_test_message ("Authenticating");
soup_auth_authenticate (auth, "username", "password");
return G_SOURCE_REMOVE;
}
static gboolean
on_authenticate (SoupMessage *msg, SoupAuth *auth, gboolean retrying, gpointer user_data)
{
g_test_message ("Authenticate request");
/* Force it to pause the message by delaying auth */
g_timeout_add (500, (GSourceFunc)on_delayed_auth, auth);
return TRUE;
}
static void
do_paused_async_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GBytes *response;
GError *error = NULL;
uri = g_uri_parse_relative (base_uri, "/auth", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
g_signal_connect (msg, "authenticate", G_CALLBACK (on_authenticate), NULL);
response = soup_test_session_async_send (test->session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Authenticated");
g_bytes_unref (response);
g_object_unref (msg);
g_uri_unref (uri);
}
static SoupConnection *last_connection;
static void
on_send_ready (GObject *source, GAsyncResult *res, gpointer user_data)
{
SoupSession *sess = SOUP_SESSION (source);
SoupMessage *msg = soup_session_get_async_result_message (sess, res);
guint *complete_count = user_data;
SoupConnection *conn;
GError *error = NULL;
GInputStream *stream;
stream = soup_session_send_finish (sess, res, &error);
g_assert_no_error (error);
g_assert_nonnull (stream);
GBytes *result = read_stream_to_bytes_sync (stream);
g_object_unref (stream);
g_assert_nonnull (result);
g_assert_cmpstr (g_bytes_get_data (result, NULL), ==, "Hello world");
g_bytes_unref (result);
g_assert_nonnull (msg);
g_assert_cmpuint (soup_message_get_http_version (msg), ==, SOUP_HTTP_2_0);
conn = soup_message_get_connection (msg);
if (last_connection)
g_assert (last_connection == conn);
else
last_connection = conn;
g_test_message ("Conn (%u) = %p", *complete_count, conn);
*complete_count += 1;
}
static void
do_connections_test (Test *test, gconstpointer data)
{
GMainContext *async_context;
guint complete_count = 0;
GUri *uri;
if (g_getenv ("ASAN_OPTIONS")) {
g_test_skip ("Flakey on asan GitLab runner");
return;
}
async_context = g_main_context_ref_thread_default ();
uri = g_uri_parse_relative (base_uri, "/slow", SOUP_HTTP_URI_FLAGS, NULL);
#define N_TESTS 100
for (unsigned int i = 0; i < N_TESTS; ++i) {
SoupMessage *msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
soup_session_send_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, on_send_ready, &complete_count);
g_object_unref (msg);
}
while (complete_count != N_TESTS) {
g_main_context_iteration (async_context, TRUE);
}
while (g_main_context_pending (async_context))
g_main_context_iteration (async_context, FALSE);
/* After no messages reference the connection it should be IDLE and reusable */
g_assert_cmpuint (soup_connection_get_state (last_connection), ==, SOUP_CONNECTION_IDLE);
SoupMessage *msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
soup_session_send_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, on_send_ready, &complete_count);
g_object_unref (msg);
while (complete_count != N_TESTS + 1)
g_main_context_iteration (async_context, TRUE);
while (g_main_context_pending (async_context))
g_main_context_iteration (async_context, FALSE);
g_uri_unref (uri);
g_main_context_unref (async_context);
}
static void
do_misdirected_request_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GBytes *response;
GError *error = NULL;
uri = g_uri_parse_relative (base_uri, "/misdirected_request", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
response = soup_test_session_async_send (test->session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Success!");
g_bytes_unref (response);
g_object_unref (msg);
g_uri_unref (uri);
}
static void
log_printer (SoupLogger *logger,
SoupLoggerLogLevel level,
char direction,
const char *data,
gpointer user_data)
{
gboolean *has_logged_body = user_data;
// We are testing that the request body is logged
// which is backend specific for now
if (direction == '>' && g_strcmp0 (data, "Test") == 0)
*has_logged_body = TRUE;
}
static void
do_logging_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GBytes *response;
GError *error = NULL;
GBytes *bytes = g_bytes_new_static ("Test", sizeof ("Test"));
gboolean has_logged_body = FALSE;
SoupLogger *logger = soup_logger_new (SOUP_LOGGER_LOG_BODY);
soup_logger_set_printer (logger, log_printer, &has_logged_body, NULL);
soup_session_add_feature (test->session, SOUP_SESSION_FEATURE (logger));
uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri);
soup_message_set_request_body_from_bytes (msg, "text/plain", bytes);
response = soup_test_session_async_send (test->session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Test");
g_assert_true (has_logged_body);
g_bytes_unref (response);
g_object_unref (msg);
g_uri_unref (uri);
}
static void
msg_got_body_data_cb (SoupMessage *msg,
guint chunk_size,
guint64 *response_body_bytes_received)
{
*response_body_bytes_received += chunk_size;
}
static void
do_metrics_size_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GBytes *response;
GError *error = NULL;
guint64 response_body_bytes_received = 0;
GBytes *bytes = g_bytes_new_static ("Test", sizeof ("Test"));
uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri);
g_signal_connect (msg, "got-body-data",
G_CALLBACK (msg_got_body_data_cb),
&response_body_bytes_received);
soup_message_set_request_body_from_bytes (msg, "text/plain", bytes);
soup_message_add_flags (msg, SOUP_MESSAGE_COLLECT_METRICS);
response = soup_test_session_async_send (test->session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Test");
SoupMessageMetrics *metrics = soup_message_get_metrics (msg);
g_assert_nonnull (metrics);
g_assert_cmpuint (soup_message_metrics_get_request_header_bytes_sent (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_request_body_size (metrics), ==, g_bytes_get_size (bytes));
g_assert_cmpuint (soup_message_metrics_get_request_body_bytes_sent (metrics), >, soup_message_metrics_get_request_body_size (metrics));
g_assert_cmpuint (soup_message_metrics_get_response_header_bytes_received (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_response_body_size (metrics), ==, g_bytes_get_size (response));
g_assert_cmpuint (soup_message_metrics_get_response_body_bytes_received (metrics), >, soup_message_metrics_get_response_body_size (metrics));
g_assert_cmpuint (soup_message_metrics_get_response_body_bytes_received (metrics), ==, response_body_bytes_received);
g_bytes_unref (response);
g_bytes_unref (bytes);
g_object_unref (msg);
g_uri_unref (uri);
}
static void
metrics_test_network_event_cb (SoupMessage *msg,
GSocketClientEvent event,
GIOStream *connection,
guint *network_event_called)
{
SoupMessageMetrics *metrics = soup_message_get_metrics (msg);
g_assert_cmpuint (soup_message_metrics_get_fetch_start (metrics), >, 0);
switch (event) {
case G_SOCKET_CLIENT_RESOLVING:
g_assert_cmpuint (soup_message_metrics_get_dns_start (metrics), >, 0);
break;
case G_SOCKET_CLIENT_RESOLVED:
g_assert_cmpuint (soup_message_metrics_get_dns_end (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_dns_end (metrics), >=, soup_message_metrics_get_dns_start (metrics));
break;
case G_SOCKET_CLIENT_CONNECTING:
g_assert_cmpuint (soup_message_metrics_get_connect_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_connect_start (metrics), >=, soup_message_metrics_get_dns_end (metrics));
break;
case G_SOCKET_CLIENT_TLS_HANDSHAKING:
g_assert_cmpuint (soup_message_metrics_get_tls_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_tls_start (metrics), >=, soup_message_metrics_get_connect_start (metrics));
break;
case G_SOCKET_CLIENT_COMPLETE:
g_assert_cmpuint (soup_message_metrics_get_connect_end (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_connect_end (metrics), >=, soup_message_metrics_get_connect_start (metrics));
g_assert_cmpuint (soup_message_metrics_get_connect_end (metrics), >=, soup_message_metrics_get_tls_start (metrics));
break;
default:
return;
}
*network_event_called += 1;
}
static void
metrics_test_message_starting_cb (SoupMessage *msg,
gboolean *starting_called)
{
SoupMessageMetrics *metrics = soup_message_get_metrics (msg);
g_assert_cmpuint (soup_message_metrics_get_request_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_request_start (metrics), >=, soup_message_metrics_get_fetch_start (metrics));
*starting_called = TRUE;
}
static void
metrics_test_status_changed_cb (SoupMessage *msg,
GParamSpec *pspec,
gboolean *status_changed_called)
{
SoupMessageMetrics *metrics = soup_message_get_metrics (msg);
g_assert_cmpuint (soup_message_metrics_get_response_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_response_start (metrics), >=, soup_message_metrics_get_request_start (metrics));
*status_changed_called = TRUE;
}
static void
metrics_test_got_body_cb (SoupMessage *msg,
gboolean *got_body_called)
{
SoupMessageMetrics *metrics = soup_message_get_metrics (msg);
g_assert_cmpuint (soup_message_metrics_get_response_end (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_response_end (metrics), >=, soup_message_metrics_get_response_start (metrics));
*got_body_called = TRUE;
}
static void
do_one_metrics_time_test (SoupSession *session,
gboolean is_new_connection)
{
SoupMessage *msg;
GBytes *body;
SoupMessageMetrics *metrics;
gboolean starting_called = FALSE;
gboolean status_changed_called = FALSE;
gboolean got_body_called = FALSE;
guint network_event_called = 0;
msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri);
soup_message_add_flags (msg, SOUP_MESSAGE_COLLECT_METRICS);
g_signal_connect (msg, "starting",
G_CALLBACK (metrics_test_message_starting_cb),
&starting_called);
g_signal_connect (msg, "notify::status-code",
G_CALLBACK (metrics_test_status_changed_cb),
&status_changed_called);
g_signal_connect (msg, "got-body",
G_CALLBACK (metrics_test_got_body_cb),
&got_body_called);
g_signal_connect (msg, "network-event",
G_CALLBACK (metrics_test_network_event_cb),
&network_event_called);
body = soup_session_send_and_read (session, msg, NULL, NULL);
soup_test_assert_message_status (msg, SOUP_STATUS_OK);
g_bytes_unref (body);
g_assert_true (starting_called);
g_assert_true (status_changed_called);
g_assert_true (got_body_called);
if (is_new_connection)
g_assert_cmpuint (network_event_called, ==, 5);
else
g_assert_cmpuint (network_event_called, ==, 0);
metrics = soup_message_get_metrics (msg);
g_assert_nonnull (metrics);
g_assert_cmpuint (soup_message_metrics_get_fetch_start (metrics), >, 0);
if (is_new_connection) {
g_assert_cmpuint (soup_message_metrics_get_dns_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_dns_end (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_connect_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_tls_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_connect_end (metrics), >, 0);
} else {
g_assert_cmpuint (soup_message_metrics_get_dns_start (metrics), ==, 0);
g_assert_cmpuint (soup_message_metrics_get_dns_end (metrics), ==, 0);
g_assert_cmpuint (soup_message_metrics_get_connect_start (metrics), ==, 0);
g_assert_cmpuint (soup_message_metrics_get_tls_start (metrics), ==, 0);
g_assert_cmpuint (soup_message_metrics_get_connect_end (metrics), ==, 0);
}
g_assert_cmpuint (soup_message_metrics_get_request_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_response_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_response_end (metrics), >, 0);
g_object_unref (msg);
}
static void
do_metrics_time_test (Test *test, gconstpointer data)
{
do_one_metrics_time_test (test->session, TRUE);
do_one_metrics_time_test (test->session, FALSE);
}
static void
on_preconnect_ready (SoupSession *session,
GAsyncResult *result,
SoupConnection **conn)
{
SoupMessage *msg = soup_session_get_async_result_message (session, result);
GError *error = NULL;
*conn = soup_message_get_connection (msg);
soup_session_preconnect_finish (session, result, &error);
g_assert_no_error (error);
}
static void
do_preconnect_test (Test *test, gconstpointer data)
{
GMainContext *async_context = g_main_context_ref_thread_default ();
SoupMessage *msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri);
GError *error = NULL;
SoupConnection *conn = NULL;
guint32 connection_id;
soup_session_preconnect_async (test->session, msg, G_PRIORITY_DEFAULT, NULL,
(GAsyncReadyCallback)on_preconnect_ready,
&conn);
while (!conn)
g_main_context_iteration (async_context, FALSE);
connection_id = soup_message_get_connection_id (msg);
g_assert_cmpuint (soup_connection_get_state (conn), ==, SOUP_CONNECTION_IDLE);
g_object_unref (msg);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri);
GBytes *response = soup_test_session_async_send (test->session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Hello world");
g_assert_cmpuint (soup_message_get_connection_id (msg), ==, connection_id);
g_bytes_unref (response);
g_object_unref (msg);
g_main_context_unref (async_context);
}
static void
do_invalid_header_test (Test *test, gconstpointer data)
{
static const char *invalid_headers[] = { "Connection", "Keep-Alive", "Proxy-Connection", "Transfer-Encoding", "Upgrade" };
guint i;
for (i = 0; i < G_N_ELEMENTS (invalid_headers); i++) {
SoupMessage *msg;
SoupMessageHeaders *request_headers;
GBytes *body;
GError *error = NULL;
msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri);
request_headers = soup_message_get_request_headers (msg);
soup_message_headers_append (request_headers, invalid_headers[i], "Value");
body = soup_test_session_async_send (test->session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_cmpstr (g_bytes_get_data (body, NULL), ==, "Hello world");
g_bytes_unref (body);
g_object_unref (msg);
}
}
static void
do_invalid_header_received_test (Test *test, gconstpointer data)
{
gboolean async = GPOINTER_TO_INT (data);
GUri *uri;
SoupMessage *msg;
GBytes *response;
GError *error = NULL;
uri = g_uri_parse_relative (base_uri, "/invalid-header", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
if (async)
response = soup_test_session_async_send (test->session, msg, NULL, &error);
else
response = soup_session_send_and_read (test->session, msg, NULL, &error);
g_assert_null (response);
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED);
g_assert_cmpstr (error->message, ==, "HTTP/2 Error: PROTOCOL_ERROR");
g_clear_error (&error);
g_uri_unref (uri);
g_object_unref (msg);
}
#ifdef HAVE_NGHTTP2_OPTION_SET_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION
static void
do_invalid_header_rfc9113_received_test (Test *test, gconstpointer data)
{
gboolean async = GPOINTER_TO_INT (data);
GUri *uri;
SoupMessage *msg;
GBytes *response;
GError *error = NULL;
uri = g_uri_parse_relative (base_uri, "/invalid-header-rfc9113", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
if (async)
response = soup_test_session_async_send (test->session, msg, NULL, &error);
else
response = soup_session_send_and_read (test->session, msg, NULL, &error);
g_assert_nonnull (response);
g_assert_no_error (error);
g_clear_error (&error);
g_object_unref (msg);
g_uri_unref (uri);
}
#endif
static void
content_sniffed (SoupMessage *msg,
const char *content_type,
GHashTable *params,
char **sniffed_type)
{
g_object_set_data (G_OBJECT (msg), "content-sniffed", GINT_TO_POINTER (TRUE));
*sniffed_type = g_strdup (content_type);
}
static void
got_headers (SoupMessage *msg)
{
soup_test_assert (g_object_get_data (G_OBJECT (msg), "content-sniffed") == NULL,
"content-sniffed got emitted before got-headers");
g_object_set_data (G_OBJECT (msg), "got-headers", GINT_TO_POINTER (TRUE));
}
static void
sniffer_test_send_ready_cb (SoupSession *session,
GAsyncResult *result,
GInputStream **stream)
{
GError *error = NULL;
*stream = soup_session_send_finish (session, result, &error);
g_assert_no_error (error);
g_assert_nonnull (*stream);
}
static void
do_one_sniffer_test (SoupSession *session,
const char *path,
gsize expected_size,
gboolean should_sniff,
GMainContext *async_context)
{
GUri *uri;
SoupMessage *msg;
GInputStream *stream = NULL;
GBytes *bytes;
char *sniffed_type = NULL;
uri = g_uri_parse_relative (base_uri, path, SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
g_object_connect (msg,
"signal::got-headers", got_headers, NULL,
"signal::content-sniffed", content_sniffed, &sniffed_type,
NULL);
if (async_context) {
soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, NULL,
(GAsyncReadyCallback)sniffer_test_send_ready_cb,
&stream);
while (!stream)
g_main_context_iteration (async_context, TRUE);
} else {
GError *error = NULL;
stream = soup_session_send (session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (stream);
}
if (should_sniff) {
soup_test_assert (g_object_get_data (G_OBJECT (msg), "content-sniffed") != NULL,
"content-sniffed did not get emitted");
g_assert_cmpstr (sniffed_type, ==, "text/plain");
} else {
soup_test_assert (g_object_get_data (G_OBJECT (msg), "content-sniffed") == NULL,
"content-sniffed got emitted without a sniffer");
g_assert_null (sniffed_type);
}
bytes = read_stream_to_bytes_sync (stream);
g_assert_cmpuint (g_bytes_get_size (bytes), ==, expected_size);
g_free (sniffed_type);
g_object_unref (stream);
g_bytes_unref (bytes);
g_object_unref (msg);
g_uri_unref (uri);
}
static void
do_sniffer_async_test (Test *test, gconstpointer data)
{
GMainContext *async_context = g_main_context_ref_thread_default ();
gboolean should_content_sniff = GPOINTER_TO_INT (data);
if (should_content_sniff)
soup_session_add_feature_by_type (test->session, SOUP_TYPE_CONTENT_SNIFFER);
do_one_sniffer_test (test->session, "/", 11, should_content_sniff, async_context);
do_one_sniffer_test (test->session, "/large", (LARGE_N_CHARS * LARGE_CHARS_REPEAT) + 1, should_content_sniff, async_context);
do_one_sniffer_test (test->session, "/no-content", 0, should_content_sniff, async_context);
while (g_main_context_pending (async_context))
g_main_context_iteration (async_context, FALSE);
g_main_context_unref (async_context);
}
static void
do_sniffer_sync_test (Test *test, gconstpointer data)
{
gboolean should_content_sniff = GPOINTER_TO_INT (data);
if (should_content_sniff)
soup_session_add_feature_by_type (test->session, SOUP_TYPE_CONTENT_SNIFFER);
do_one_sniffer_test (test->session, "/", 11, should_content_sniff, NULL);
do_one_sniffer_test (test->session, "/large", (LARGE_N_CHARS * LARGE_CHARS_REPEAT) + 1, should_content_sniff, NULL);
do_one_sniffer_test (test->session, "/no-content", 0, should_content_sniff, NULL);
}
static void
do_timeout_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GBytes *response;
GError *error = NULL;
soup_session_set_timeout (test->session, 2);
uri = g_uri_parse_relative (base_uri, "/timeout", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
response = soup_test_session_async_send (test->session, msg, NULL, &error);
g_assert_null (response);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
g_object_unref (msg);
g_uri_unref (uri);
while (g_main_context_pending (NULL))
g_main_context_iteration (NULL, FALSE);
}
static void
do_connection_closed_test (Test *test, gconstpointer data)
{
GUri *uri;
SoupMessage *msg;
GInputStream *stream;
GError *error = NULL;
uri = g_uri_parse_relative (base_uri, "/close", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
stream = soup_session_send (test->session, msg, NULL, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT);
g_clear_error (&error);
g_clear_object (&stream);
g_object_unref (msg);
g_uri_unref (uri);
}
static gboolean
unpause_message (SoupServerMessage *msg)
{
soup_server_message_unpause (msg);
g_object_unref (msg);
return FALSE;
}
static void
server_handler (SoupServer *server,
SoupServerMessage *msg,
const char *path,
GHashTable *query,
gpointer user_data)
{
g_assert_cmpuint (soup_server_message_get_http_version (msg), ==, SOUP_HTTP_2_0);
if (strcmp (path, "/") == 0 || strcmp (path, "/slow") == 0 || strcmp (path, "/timeout") == 0) {
gboolean is_slow = path[1] == 's';
gboolean is_timeout = path[1] == 't';
soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
soup_server_message_set_response (msg, "text/plain",
SOUP_MEMORY_STATIC,
"Hello world", 11);
if (is_slow || is_timeout) {
GSource *timeout;
soup_server_message_pause (msg);
timeout = soup_add_timeout (g_main_context_get_thread_default (),
is_timeout ? 4000 : 1000,
(GSourceFunc)unpause_message, g_object_ref (msg));
g_source_unref (timeout);
}
} else if (strcmp (path, "/no-content") == 0) {
soup_server_message_set_status (msg, SOUP_STATUS_NO_CONTENT, NULL);
} else if (strcmp (path, "/large") == 0) {
int i, j;
SoupMessageBody *response_body;
char letter = 'A';
/* Send increasing letters just to aid debugging */
response_body = soup_server_message_get_response_body (msg);
for (i = 0; i < LARGE_N_CHARS; i++, letter++) {
GString *chunk = g_string_new (NULL);
for (j = 0; j < LARGE_CHARS_REPEAT; j++)
chunk = g_string_append_c (chunk, letter);
soup_message_body_append_bytes (response_body, g_string_free_to_bytes (chunk));
}
soup_message_body_append (response_body, SOUP_MEMORY_STATIC, "\0", 1);
soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
} else if (strcmp (path, "/echo_query") == 0) {
const char *query_str = g_uri_get_query (soup_server_message_get_uri (msg));
soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
soup_server_message_set_response (msg, "text/plain",
SOUP_MEMORY_STATIC,
query_str, strlen (query_str));
} else if (strcmp (path, "/echo_post") == 0) {
SoupMessageBody *request_body;
request_body = soup_server_message_get_request_body (msg);
soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
soup_server_message_set_response (msg, "text/plain",
SOUP_MEMORY_COPY,
request_body->data,
request_body->length);
} else if (strcmp (path, "/misdirected_request") == 0) {
static SoupServerConnection *conn = NULL;
if (!conn) {
conn = soup_server_message_get_connection (msg);
soup_server_message_set_status (msg, SOUP_STATUS_MISDIRECTED_REQUEST, NULL);
} else {
/* Message is retried on a different connection */
g_assert_false (conn == soup_server_message_get_connection (msg));
soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
soup_server_message_set_response (msg, "text/plain",
SOUP_MEMORY_STATIC,
"Success!", 8);
}
} else if (strcmp (path, "/auth") == 0) {
soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
soup_server_message_set_response (msg, "text/plain",
SOUP_MEMORY_STATIC,
"Authenticated", 13);
} else if (strcmp (path, "/invalid-header") == 0) {
SoupMessageHeaders *response_headers;
response_headers = soup_server_message_get_response_headers (msg);
/* Use soup_message_headers_append_common to skip the validation check. */
soup_message_headers_append_common (response_headers, SOUP_HEADER_CONTENT_TYPE, "\r");
soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
} else if (strcmp (path, "/invalid-header-rfc9113") == 0) {
SoupMessageHeaders *response_headers;
response_headers = soup_server_message_get_response_headers (msg);
soup_message_headers_append (response_headers, "Invalid-Header-Value", "foo ");
soup_server_message_set_response (msg, "text/plain",
SOUP_MEMORY_STATIC,
"Success!", 8);
soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
} else if (strcmp (path, "/close") == 0) {
SoupServerConnection *conn;
int fd;
conn = soup_server_message_get_connection (msg);
fd = g_socket_get_fd (soup_server_connection_get_socket (conn));
#ifdef G_OS_WIN32
shutdown (fd, SD_SEND);
#else
shutdown (fd, SHUT_WR);
#endif
soup_server_message_set_response (msg, "text/plain",
SOUP_MEMORY_STATIC,
"Success!", 8);
}
}
static gboolean
server_basic_auth_callback (SoupAuthDomain *auth_domain,
SoupServerMessage *msg,
const char *username,
const char *password,
gpointer data)
{
if (strcmp (username, "username") != 0)
return FALSE;
return strcmp (password, "password") == 0;
}
int
main (int argc, char **argv)
{
SoupServer *server;
SoupAuthDomain *auth;
int ret;
test_init (argc, argv, NULL);
if (!tls_available)
return 0;
server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD | SOUP_TEST_SERVER_HTTP2);
auth = soup_auth_domain_basic_new ("realm", "http2-test",
"auth-callback", server_basic_auth_callback,
NULL);
soup_auth_domain_add_path (auth, "/auth");
soup_server_add_auth_domain (server, auth);
g_object_unref (auth);
soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
base_uri = soup_test_server_get_uri (server, "https", "127.0.0.1");
g_test_add ("/http2/basic/async", Test, NULL,
setup_session,
do_basic_async_test,
teardown_session);
g_test_add ("/http2/basic/sync", Test, NULL,
setup_session,
do_basic_sync_test,
teardown_session);
g_test_add ("/http2/no_content/async", Test, NULL,
setup_session,
do_no_content_async_test,
teardown_session);
g_test_add ("/http2/large/async", Test, GINT_TO_POINTER (TRUE),
setup_session,
do_large_test,
teardown_session);
g_test_add ("/http2/large/sync", Test, GINT_TO_POINTER (FALSE),
setup_session,
do_large_test,
teardown_session);
g_test_add ("/http2/multiplexing/async", Test, NULL,
setup_session,
do_multi_message_async_test,
teardown_session);
g_test_add ("/http2/post/async", Test, NULL,
setup_session,
do_post_async_test,
teardown_session);
g_test_add ("/http2/post/sync", Test, NULL,
setup_session,
do_post_sync_test,
teardown_session);
g_test_add ("/http2/post/large/sync", Test, NULL,
setup_session,
do_post_large_sync_test,
teardown_session);
g_test_add ("/http2/post/large/async", Test, NULL,
setup_session,
do_post_large_async_test,
teardown_session);
g_test_add ("/http2/post/blocked/async", Test, NULL,
setup_session,
do_post_blocked_async_test,
teardown_session);
g_test_add ("/http2/post/file/async", Test, NULL,
setup_session,
do_post_file_async_test,
teardown_session);
g_test_add ("/http2/paused/async", Test, NULL,
setup_session,
do_paused_async_test,
teardown_session);
g_test_add ("/http2/connections", Test, NULL,
setup_session,
do_connections_test,
teardown_session);
g_test_add ("/http2/misdirected_request", Test, NULL,
setup_session,
do_misdirected_request_test,
teardown_session);
g_test_add ("/http2/logging", Test, NULL,
setup_session,
do_logging_test,
teardown_session);
g_test_add ("/http2/metrics/size", Test, NULL,
setup_session,
do_metrics_size_test,
teardown_session);
g_test_add ("/http2/metrics/time", Test, NULL,
setup_session,
do_metrics_time_test,
teardown_session);
g_test_add ("/http2/preconnect", Test, NULL,
setup_session,
do_preconnect_test,
teardown_session);
g_test_add ("/http2/cancellation", Test, NULL,
setup_session,
do_cancellation_test,
teardown_session);
g_test_add ("/http2/cancellation-after-send", Test, NULL,
setup_session,
do_cancellation_after_send_test,
teardown_session);
g_test_add ("/http2/invalid-header", Test, NULL,
setup_session,
do_invalid_header_test,
teardown_session);
g_test_add ("/http2/invalid-header-received/async", Test, GINT_TO_POINTER (TRUE),
setup_session,
do_invalid_header_received_test,
teardown_session);
g_test_add ("/http2/invalid-header-received/sync", Test, GINT_TO_POINTER (FALSE),
setup_session,
do_invalid_header_received_test,
teardown_session);
#ifdef HAVE_NGHTTP2_OPTION_SET_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION
g_test_add ("/http2/invalid-header-rfc9113-received/async", Test, GINT_TO_POINTER (TRUE),
setup_session,
do_invalid_header_rfc9113_received_test,
teardown_session);
g_test_add ("/http2/invalid-header-rfc9113-received/sync", Test, GINT_TO_POINTER (FALSE),
setup_session,
do_invalid_header_rfc9113_received_test,
teardown_session);
#endif
g_test_add ("/http2/sniffer/with-sniffer/async", Test, GINT_TO_POINTER (TRUE),
setup_session,
do_sniffer_async_test,
teardown_session);
g_test_add ("/http2/sniffer/no-sniffer/async", Test, GINT_TO_POINTER (FALSE),
setup_session,
do_sniffer_async_test,
teardown_session);
g_test_add ("/http2/sniffer/with-sniffer/sync", Test, GINT_TO_POINTER (TRUE),
setup_session,
do_sniffer_sync_test,
teardown_session);
g_test_add ("/http2/sniffer/no-sniffer/sync", Test, GINT_TO_POINTER (FALSE),
setup_session,
do_sniffer_sync_test,
teardown_session);
g_test_add ("/http2/timeout", Test, NULL,
setup_session,
do_timeout_test,
teardown_session);
g_test_add ("/http2/connection-closed", Test, NULL,
setup_session,
do_connection_closed_test,
teardown_session);
ret = g_test_run ();
g_uri_unref (base_uri);
soup_test_server_quit_unref (server);
test_cleanup ();
return ret;
}